[
  {
    "path": ".gitattributes",
    "content": "# Auto detect text files and perform LF normalization\n\n* text=auto\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Report a bug and help us to improve Serilog.Expressions\ntitle: ''\nlabels: bug\nassignees: ''\n\n---\n\nThe Serilog maintainers want you to have a great experience using Serilog.Expressions, and will happily track down and resolve bugs. We all have limited time, though, so please think through all of the factors that might be involved and include as much useful information as possible 😊.\n\nℹ If the problem is caused by a sink or other extension package, please track down the correct repository for that package and create the report there: this tracker is for the **Serilog.Expressions** package only.\n\n**Description**\nWhat's going wrong?\n\n**Reproduction**\nPlease provide code samples showing how you're configuring and calling Serilog.Expressions to produce the behavior.\n\n**Expected behavior**\nA concise description of what you expected to happen.\n\n**Relevant package, tooling and runtime versions**\nWhat Serilog version are you using, on what platform?\n\n**Additional context**\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "contact_links:\n  - name: Ask for help\n    url: https://stackoverflow.com/tags/serilog\n    about: Ask the community for help on how to use Serilog.Expressions\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an improvement to Serilog.Expressions\ntitle: ''\nlabels: enhancement\nassignees: ''\n\n---\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. For example, \"I'd like to do _x_ but currently I can't because _y_ [...]\".\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any workarounds or alternative solutions you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "# If this file is renamed, the incrementing run attempt number will be reset.\n\nname: CI\n\non:\n  push:\n    branches: [ \"dev\", \"main\" ]\n  pull_request:\n    branches: [ \"dev\", \"main\" ]\n\nenv:\n  CI_BUILD_NUMBER_BASE: ${{ github.run_number }}\n  CI_TARGET_BRANCH: ${{ github.head_ref || github.ref_name }}\n\njobs:\n  build:\n\n    # The build must run on Windows so that .NET Framework targets can be built and tested.\n    runs-on: windows-latest\n\n    permissions:\n      contents: write\n\n    steps:\n      - uses: actions/checkout@v4\n      - name: Setup\n        uses: actions/setup-dotnet@v4\n        with:\n          dotnet-version: 9.0.x\n      - name: Compute build number\n        shell: bash\n        run: |\n          echo \"CI_BUILD_NUMBER=$(($CI_BUILD_NUMBER_BASE+2300))\" >> $GITHUB_ENV\n      - name: Build and Publish\n        env:\n          DOTNET_CLI_TELEMETRY_OPTOUT: true\n          NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}\n          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        shell: pwsh\n        run: |\n          ./Build.ps1\n"
  },
  {
    "path": ".gitignore",
    "content": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n\n# User-specific files\n*.suo\n*.user\n*.userosscache\n*.sln.docstates\n.idea\n\n# User-specific files (MonoDevelop/Xamarin Studio)\n*.userprefs\n\n# Build results\n[Dd]ebug/\n[Dd]ebugPublic/\n[Rr]elease/\n[Rr]eleases/\nx64/\nx86/\nbld/\n[Bb]in/\n[Oo]bj/\n\n# Visual Studio 2015 cache/options directory\n.vs/\n# Uncomment if you have tasks that create the project's static files in wwwroot\n#wwwroot/\n\n# MSTest test Results\n[Tt]est[Rr]esult*/\n[Bb]uild[Ll]og.*\n\n# NUNIT\n*.VisualState.xml\nTestResult.xml\n\n# Build Results of an ATL Project\n[Dd]ebugPS/\n[Rr]eleasePS/\ndlldata.c\n\n# DNX\nproject.lock.json\nartifacts/\n\n*_i.c\n*_p.c\n*_i.h\n*.ilk\n*.meta\n*.obj\n*.pch\n*.pdb\n*.pgc\n*.pgd\n*.rsp\n*.sbr\n*.tlb\n*.tli\n*.tlh\n*.tmp\n*.tmp_proj\n*.log\n*.vspscc\n*.vssscc\n.builds\n*.pidb\n*.svclog\n*.scc\n\n# Chutzpah Test files\n_Chutzpah*\n\n# Visual C++ cache files\nipch/\n*.aps\n*.ncb\n*.opendb\n*.opensdf\n*.sdf\n*.cachefile\n\n# Visual Studio profiler\n*.psess\n*.vsp\n*.vspx\n*.sap\n\n# TFS 2012 Local Workspace\n$tf/\n\n# Guidance Automation Toolkit\n*.gpState\n\n# ReSharper is a .NET coding add-in\n_ReSharper*/\n*.[Rr]e[Ss]harper\n*.DotSettings.user\n\n# JustCode is a .NET coding add-in\n.JustCode\n\n# TeamCity is a build add-in\n_TeamCity*\n\n# DotCover is a Code Coverage Tool\n*.dotCover\n\n# NCrunch\n_NCrunch_*\n.*crunch*.local.xml\nnCrunchTemp_*\n\n# MightyMoose\n*.mm.*\nAutoTest.Net/\n\n# Web workbench (sass)\n.sass-cache/\n\n# Installshield output folder\n[Ee]xpress/\n\n# DocProject is a documentation generator add-in\nDocProject/buildhelp/\nDocProject/Help/*.HxT\nDocProject/Help/*.HxC\nDocProject/Help/*.hhc\nDocProject/Help/*.hhk\nDocProject/Help/*.hhp\nDocProject/Help/Html2\nDocProject/Help/html\n\n# Click-Once directory\npublish/\n\n# Publish Web Output\n*.[Pp]ublish.xml\n*.azurePubxml\n# TODO: Comment the next line if you want to checkin your web deploy settings \n# but database connection strings (with potential passwords) will be unencrypted\n*.pubxml\n*.publishproj\n\n# NuGet Packages\n*.nupkg\n# The packages folder can be ignored because of Package Restore\n**/packages/*\n# except build/, which is used as an MSBuild target.\n!**/packages/build/\n# Uncomment if necessary however generally it will be regenerated when needed\n#!**/packages/repositories.config\n# NuGet v3's project.json files produces more ignoreable files\n*.nuget.props\n*.nuget.targets\n\n# Microsoft Azure Build Output\ncsx/\n*.build.csdef\n\n# Microsoft Azure Emulator\necf/\nrcf/\n\n# Microsoft Azure ApplicationInsights config file\nApplicationInsights.config\n\n# Windows Store app package directory\nAppPackages/\nBundleArtifacts/\n\n# Visual Studio cache files\n# files ending in .cache can be ignored\n*.[Cc]ache\n# but keep track of directories ending in .cache\n!*.[Cc]ache/\n\n# Others\nClientBin/\n~$*\n*~\n*.dbmdl\n*.dbproj.schemaview\n*.pfx\n*.publishsettings\nnode_modules/\norleans.codegen.cs\n\n# RIA/Silverlight projects\nGenerated_Code/\n\n# Backup & report files from converting an old project file\n# to a newer Visual Studio version. Backup files are not needed,\n# because we have git ;-)\n_UpgradeReport_Files/\nBackup*/\nUpgradeLog*.XML\nUpgradeLog*.htm\n\n# SQL Server files\n*.mdf\n*.ldf\n\n# Business Intelligence projects\n*.rdl.data\n*.bim.layout\n*.bim_*.settings\n\n# Microsoft Fakes\nFakesAssemblies/\n\n# GhostDoc plugin setting file\n*.GhostDoc.xml\n\n# Node.js Tools for Visual Studio\n.ntvs_analysis.dat\n\n# Visual Studio 6 build log\n*.plg\n\n# Visual Studio 6 workspace options file\n*.opt\n\n# Visual Studio LightSwitch build output\n**/*.HTMLClient/GeneratedArtifacts\n**/*.DesktopClient/GeneratedArtifacts\n**/*.DesktopClient/ModelManifest.xml\n**/*.Server/GeneratedArtifacts\n**/*.Server/ModelManifest.xml\n_Pvt_Extensions\n\n# Paket dependency manager\n.paket/paket.exe\n\n# FAKE - F# Make\n.fake/\nexample/Sample/log.txt\nBenchmarkDotNet.Artifacts/\n"
  },
  {
    "path": "Build.ps1",
    "content": "Write-Output \"build: Tool versions follow\"\n\ndotnet --version\ndotnet --list-sdks\n\nWrite-Output \"build: Build started\"\n\nPush-Location $PSScriptRoot\ntry {\n    if(Test-Path .\\artifacts) {\n        Write-Output \"build: Cleaning ./artifacts\"\n        Remove-Item ./artifacts -Force -Recurse\n    }\n\n    & dotnet restore --no-cache\n\n    $dbp = [Xml] (Get-Content .\\Directory.Version.props)\n    $versionPrefix = $dbp.Project.PropertyGroup.VersionPrefix\n\n    Write-Output \"build: Package version prefix is $versionPrefix\"\n\n    $branch = @{ $true = $env:CI_TARGET_BRANCH; $false = $(git symbolic-ref --short -q HEAD) }[$NULL -ne $env:CI_TARGET_BRANCH];\n    $revision = @{ $true = \"{0:00000}\" -f [convert]::ToInt32(\"0\" + $env:CI_BUILD_NUMBER, 10); $false = \"local\" }[$NULL -ne $env:CI_BUILD_NUMBER];\n    $suffix = @{ $true = \"\"; $false = \"$($branch.Substring(0, [math]::Min(10,$branch.Length)) -replace '([^a-zA-Z0-9\\-]*)', '')-$revision\"}[$branch -eq \"main\" -and $revision -ne \"local\"]\n    $commitHash = $(git rev-parse --short HEAD)\n    $buildSuffix = @{ $true = \"$($suffix)-$($commitHash)\"; $false = \"$($branch)-$($commitHash)\" }[$suffix -ne \"\"]\n\n    Write-Output \"build: Package version suffix is $suffix\"\n    Write-Output \"build: Build version suffix is $buildSuffix\"\n\n    & dotnet build -c Release --version-suffix=$buildSuffix /p:ContinuousIntegrationBuild=true\n    if($LASTEXITCODE -ne 0) { throw \"Build failed\" }\n\n    foreach ($src in Get-ChildItem src/*) {\n        Push-Location $src\n\n        Write-Output \"build: Packaging project in $src\"\n\n        if ($suffix) {\n            & dotnet pack -c Release --no-build --no-restore  -o ../../artifacts --version-suffix=$suffix\n        } else {\n            & dotnet pack -c Release --no-build --no-restore  -o ../../artifacts\n        }\n        if($LASTEXITCODE -ne 0) { throw \"Packaging failed\" }\n\n        Pop-Location\n    }\n\n    foreach ($test in Get-ChildItem test/*.Tests) {\n        Push-Location $test\n\n        Write-Output \"build: Testing project in $test\"\n\n        & dotnet test -c Release --no-build --no-restore\n        if($LASTEXITCODE -ne 0) { throw \"Testing failed\" }\n\n        Pop-Location\n    }\n\n    if ($env:NUGET_API_KEY) {\n        # GitHub Actions will only supply this to branch builds and not PRs. We publish\n        # builds from any branch this action targets (i.e. main and dev).\n\n        Write-Output \"build: Publishing NuGet packages\"\n\n        foreach ($nupkg in Get-ChildItem artifacts/*.nupkg) {\n            & dotnet nuget push -k $env:NUGET_API_KEY -s https://api.nuget.org/v3/index.json \"$nupkg\"\n            if($LASTEXITCODE -ne 0) { throw \"Publishing failed\" }\n        }\n\n        if (!($suffix)) {\n            Write-Output \"build: Creating release for version $versionPrefix\"\n\n            iex \"gh release create v$versionPrefix --title v$versionPrefix --generate-notes $(get-item ./artifacts/*.nupkg) $(get-item ./artifacts/*.snupkg)\"\n        }\n    }\n} finally {\n    Pop-Location\n}\n"
  },
  {
    "path": "Directory.Build.props",
    "content": "<Project>\n  <!-- Properties in this file are expected to be identical for all Serilog organization projects. If\n   a property value is project-specific, please record it in the CSPROJ file instead. -->\n  <Import Project=\"$(MSBuildThisFileDirectory)Directory.Version.props\" />\n  <PropertyGroup>\n    <LangVersion>latest</LangVersion>\n    <TreatWarningsAsErrors>True</TreatWarningsAsErrors>\n    <!-- The condition is required to support BenchmarkDotNet -->\n    <SignAssembly Condition=\"Exists('$(MSBuildThisFileDirectory)assets/Serilog.snk')\">true</SignAssembly>\n    <AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)assets/Serilog.snk</AssemblyOriginatorKeyFile>\n    <CheckEolTargetFramework>false</CheckEolTargetFramework>\n    <Nullable>enable</Nullable>\n    <ImplicitUsings>enable</ImplicitUsings>\n    <GenerateDocumentationFile>true</GenerateDocumentationFile>\n    <PublishRepositoryUrl>true</PublishRepositoryUrl>\n    <EmbedUntrackedSources>true</EmbedUntrackedSources>\n    <IncludeSymbols>true</IncludeSymbols>\n    <SymbolPackageFormat>snupkg</SymbolPackageFormat>\n  </PropertyGroup>\n  <ItemGroup Condition=\"'$(TargetFrameworkIdentifier)' == '.NETFramework'\">\n    <Reference Include=\"System\" />\n    <Reference Include=\"System.Core\" />\n    <Reference Include=\"Microsoft.CSharp\" />\n  </ItemGroup>\n</Project>\n"
  },
  {
    "path": "Directory.Version.props",
    "content": "<Project>\n  <PropertyGroup>\n    <VersionPrefix>5.1.0</VersionPrefix>\n  </PropertyGroup>\n</Project>\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "# Serilog.Expressions&nbsp;[![Build status](https://github.com/serilog/serilog-expressions/actions/workflows/ci.yml/badge.svg?branch=dev)](https://github.com/serilog/serilog-expressions/actions)&nbsp;[![NuGet Package](https://img.shields.io/nuget/vpre/serilog.expressions)](https://nuget.org/packages/serilog.expressions)\n\nAn embeddable mini-language for filtering, enriching, and formatting Serilog\nevents, ideal for use with JSON or XML configuration.\n\n## Getting started\n\nInstall the package from NuGet:\n\n```shell\ndotnet add package Serilog.Expressions\n```\n\nThe package adds extension methods to Serilog's `Filter`, `WriteTo`, and\n`Enrich` configuration objects, along with an `ExpressionTemplate`\ntype that's compatible with Serilog sinks accepting an\n`ITextFormatter`.\n\n### Filtering example\n\n_Serilog.Expressions_ adds `ByExcluding()` and `ByIncludingOnly()`\noverloads to the `Filter` configuration object that accept filter\nexpressions:\n\n```csharp\nLog.Logger = new LoggerConfiguration()\n    .Filter.ByExcluding(\"RequestPath like '/health%'\")\n    .CreateLogger();\n```\n\nEvents with a `RequestPath` property that matches the expression\nwill be excluded by the filter.\n\n> Note that if the expression syntax is invalid, an `ArgumentException` will\nbe thrown from the `ByExcluding()` method, and by similar methods elsewhere\nin the package. To check expression syntax without throwing, see the\n`Try*()` methods in the `SerilogExpression` class.\n\n#### An `appSettings.json` JSON configuration example\n\nIn [`appSettings.json` configuration](https://github.com/serilog/serilog-settings-configuration)\nthis is written as:\n\n```json\n{\n  \"Serilog\": {\n    \"Using\": [\"Serilog.Expressions\"],\n    \"Filter\": [\n      {\n        \"Name\": \"ByExcluding\",\n        \"Args\": {\n          \"expression\": \"RequestPath like '/health%'\"\n        }\n      }\n    ]\n  }\n}\n```\n\n#### An `<appSettings>` XML configuration example\n\nIn [XML configuration files](https://github.com/serilog/serilog-settings-appsettings),\nthis is written as:\n\n```xml\n  <appSettings>\n    <add key=\"serilog:using:Expressions\" value=\"Serilog.Expressions\" />\n    <add key=\"serilog:filter:ByExcluding.expression\" value=\"RequestPath like '/health%'\" />\n  </appSettings>\n```\n\n## Supported configuration APIs\n\n_Serilog.Expressions_ adds a number of expression-based overloads and helper methods to the Serilog configuration syntax:\n\n * `Filter.ByExcluding()`, `Filter.ByIncludingOnly()` - use an expression to filter events passing through the Serilog pipeline\n * `WriteTo.Conditional()` - use an expression to select the events passed to a particular sink\n * `Enrich.When()` - conditionally enable an enricher when events match an expression\n * `Enrich.WithComputed()` - add or modify event properties using an expression\n\n## Formatting with `ExpressionTemplate`\n\n_Serilog.Expressions_ includes the `ExpressionTemplate` class for text formatting. `ExpressionTemplate` implements `ITextFormatter`, so\nit works with any text-based Serilog sink, including `Console`, `File`, `Debug`, and `Email`:\n\n```csharp\n// using Serilog.Templates;\n\nLog.Logger = new LoggerConfiguration()\n    .WriteTo.Console(new ExpressionTemplate(\n        \"[{@t:HH:mm:ss} {@l:u3} ({SourceContext})] {@m} (first item is {Cart[0]})\\n{@x}\"))\n    .CreateLogger();\n\n// Produces log events like:\n// [21:21:40 INF (Sample.Program)] Cart contains [\"Tea\",\"Coffee\"] (first item is Tea)\n```\n\nTemplates are based on .NET format strings, and support standard padding, alignment, and format specifiers.\n\nAlong with standard properties for the event timestamp (`@t`), level (`@l`) and so on, \"holes\" in expression templates can include complex\nexpressions over the first-class properties of the event, like `{SourceContext}` and `{Cart[0]}` in the example..\n\nTemplates support customizable color themes when used with the `Console` sink:\n\n```csharp\n    .WriteTo.Console(new ExpressionTemplate(\n        \"[{@t:HH:mm:ss} {@l:u3}] {@m}\\n{@x}\", theme: TemplateTheme.Code))\n```\n\n![Screenshot showing colored terminal output](https://raw.githubusercontent.com/serilog/serilog-expressions/dev/assets/screenshot.png)\n\nNewline-delimited JSON (for example, replicating the [CLEF format](https://github.com/serilog/serilog-formatting-compact)) can be generated\nusing object literals:\n\n```csharp\n    .WriteTo.Console(new ExpressionTemplate(\n        \"{ {@t, @mt, @r, @l: if @l = 'Information' then undefined() else @l, @x, ..@p} }\\n\"))\n```\n\n## Language reference\n\n### Properties\n\nThe following properties are available in expressions:\n\n * **All first-class properties of the event** - no special syntax: `SourceContext` and `Cart` are used in the formatting examples above\n * `@t` - the event's timestamp, as a `DateTimeOffset`\n * `@m` - the rendered message (Note: do not add format specifiers like `:lj` or you'll lose theme color rendering. These format specifiers are not supported as they've become the default and only option - [see the discussion here](https://github.com/serilog/serilog-expressions/issues/56#issuecomment-1146472988)\n * `@mt` - the raw message template\n * `@l` - the event's level, as a `LogEventLevel`\n * `@x` - the exception associated with the event, if any, as an `Exception`\n * `@p` - a dictionary containing all first-class properties; this supports properties with non-identifier names, for example `@p['snake-case-name']`\n * `@i` - event id; a 32-bit numeric hash of the event's message template\n * `@r` - renderings; if any tokens in the message template include .NET-specific formatting, an array of rendered values for each such token\n * `@tr` - trace id; The id of the trace that was active when the event was created, if any\n * `@sp` - span id; The id of the span that was active when the event was created, if any\n\nThe built-in properties mirror those available in the CLEF format.\n\nThe exception property `@x` is treated as a scalar and will appear as a string when formatted into text. The properties of\nthe underlying `Exception` object can be accessed using `Inspect()`, for example `Inspect(@x).Message`, and the type of the\nexception retrieved using `TypeOf(@x)`.\n\n### Literals\n\n| Data type | Description | Examples |\n| :--- | :--- | :--- |\n| Null | Corresponds to .NET's `null` value | `null` |\n| Number | A number in decimal or hexadecimal notation, represented by .NET `decimal` | `0`, `100`, `-12.34`, `0xC0FFEE` |\n| String | A single-quoted Unicode string literal; to escape `'`, double it | `'pie'`, `'isn''t'`, `'😋'` |\n| Boolean | A Boolean value | `true`, `false` |\n| Array | An array of values, in square brackets | `[1, 'two', null]` |\n| Object | A mapping of string keys to values; keys that are valid identifiers do not need to be quoted | `{a: 1, 'b c': 2, d}` |\n\nArray and object literals support the spread operator: `[1, 2, ..others]`, `{a: 1, ..others}`. Specifying an undefined\nproperty in an object literal will remove it from the result: `{..User, Email: Undefined()}`\n\n### Operators and conditionals\n\nA typical set of operators is supported:\n\n * Equality `=` and inequality `<>`, including for arrays and objects\n * Boolean `and`, `or`, `not`\n * Arithmetic `+`, `-`, `*`, `/`, `^`, `%`\n * Numeric comparison `<`, `<=`, `>`, `>=`\n * Existence `is null` and `is not null`\n * SQL-style `like` and `not like`, with `%` and `_` wildcards (double wildcards to escape them)\n * Array membership with `in` and `not in`\n * Accessors `a.b`\n * Indexers `a['b']` and `a[0]`\n * Wildcard indexing - `a[?]` any, and `a[*]` all\n * Conditional `if a then b else c` (all branches required; see also the section below on _conditional blocks_)\n\nComparision operators that act on text all accept an optional postfix `ci` modifier to select case-insensitive comparisons:\n\n```\nUser.Name like 'n%' ci\n```\n\n### Functions\n\nFunctions are called using typical `Identifier(args)` syntax.\n\nExcept for the `IsDefined()` function, the result of\ncalling a function will be undefined if:\n\n * any argument is undefined, or\n * any argument is of an incompatible type.\n\n| Function                        | Description                                                                                                                                                                                                                   |\n|:--------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `Coalesce(a0, a1, [..aN])`      | Returns the first defined, non-null argument.                                                                                                                                                                                 |\n| `Concat(s0, s1, [..sN])`        | Concatenate two or more strings.                                                                                                                                                                                              |\n| `Contains(s, p)`                | Tests whether the string `s` contains the substring `p`.                                                                                                                                                                      |\n| `ElementAt(x, i)`               | Retrieves a property of `x` by name `i`, or array element of `x` by numeric index `i`.                                                                                                                                        |\n| `EndsWith(s, p)`                | Tests whether the string `s` ends with substring `p`.                                                                                                                                                                         |\n| `IndexOf(s, p)`                 | Returns the first index of substring `p` in string `s`, or -1 if the substring does not appear.                                                                                                                               |\n| `IndexOfMatch(s, p)`            | Returns the index of the first match of regular expression `p` in string `s`, or -1 if the regular expression does not match.                                                                                                 |\n| `Inspect(o, [deep])`            | Read properties from an object captured as the scalar value `o`.                                                                                                                                                              |\n| `IsMatch(s, p)`                 | Tests whether the regular expression `p` matches within the string `s`.                                                                                                                                                       |\n| `IsDefined(x)`                  | Returns `true` if the expression `x` has a value, including `null`, or `false` if `x` is undefined.                                                                                                                           |\n| `LastIndexOf(s, p)`             | Returns the last index of substring `p` in string `s`, or -1 if the substring does not appear.                                                                                                                                |\n| `Length(x)`                     | Returns the length of a string or array.                                                                                                                                                                                      |\n| `Nest(o)`                       | Converts dotted (flattened) property names of object `o` into nested sub-objects.                                                                                                                                             |\n| `Now()`                         | Returns `DateTimeOffset.Now`.                                                                                                                                                                                                 |\n| `Replace(s, p, r)`              | Replace occurrences of substring `p` in string `s` with replacement `r`.                                                                                                                                                      |\n| `Rest([deep])`                  | In an `ExpressionTemplate`, returns an object containing the first-class event properties not otherwise referenced in the template. If `deep` is `true`, also excludes properties referenced in the event's message template. |\n| `Round(n, m)`                   | Round the number `n` to `m` decimal places.                                                                                                                                                                                   |\n| `StartsWith(s, p)`              | Tests whether the string `s` starts with substring `p`.                                                                                                                                                                       |\n| `Substring(s, start, [length])` | Return the substring of string `s` from `start` to the end of the string, or of `length` characters, if this argument is supplied.                                                                                            |\n| `TagOf(o)`                      | Returns the `TypeTag` field of a captured object (i.e. where `TypeOf(x)` is `'object'`).                                                                                                                                      |\n| `ToString(x, [format])`         | Convert `x` to a string, applying the format string `format` if `x` is `IFormattable`.                                                                                                                                        |\n| `TypeOf(x)`                     | Returns a string describing the type of expression `x`: a .NET type name if `x` is scalar and non-null, or, `'array'`, `'object'`, `'dictionary'`, `'null'`, or `'undefined'`.                                                |\n| `Undefined()`                   | Explicitly mark an undefined value.                                                                                                                                                                                           |\n| `UtcDateTime(x)`                | Convert a `DateTime` or `DateTimeOffset` into a UTC `DateTime`.                                                                                                                                                               |\n\nFunctions that compare text accept an optional postfix `ci` modifier to select case-insensitive comparisons:\n\n```\nStartsWith(User.Name, 'n') ci\n```\n\n### Template directives\n\n#### Conditional blocks\n\nWithin an `ExpressionTemplate`, a portion of the template can be conditionally evaluated using `#if`.\n\n```csharp\nLog.Logger = new LoggerConfiguration()\n    .WriteTo.Console(new ExpressionTemplate(\n        \"[{@t:HH:mm:ss} {@l:u3}{#if SourceContext is not null} ({SourceContext}){#end}] {@m}\\n{@x}\"))\n    .CreateLogger();\n\n// Produces log events like:\n// [21:21:45 INF] Starting up\n// [21:21:46 INF (Sample.Program)] Firing engines\n```\n\nThe block between the `{#if <expr>}` and `{#end}` directives will only appear in the output if `<expr>` is `true` - in the example, events with a `SourceContext` include this in parentheses, while those without, don't.\n\nIt's important to notice that the directive requires a Boolean `true` before the conditional block will be evaluated. It wouldn't be sufficient in this case to write `{#if SourceContext}`, since no values other than `true` are considered \"truthy\".\n\nThe syntax supports `{#if <expr>}`, chained `{#else if <expr>}`, `{#else}`, and `{#end}`, with arbitrary nesting.\n\n#### Repetition\n\nIf a log event includes structured data in arrays or objects, a template block can be repeated for each element or member using `#each`/`in` (newlines, double quotes and construction of the `ExpressionTemplate` omitted for clarity):\n\n```\n{@l:w4}: {SourceContext}\n      {#each s in Scope}=> {s}{#delimit} {#end}\n      {@m}\n{@x}\n```\n\nThis example uses the optional `#delimit` to add a space between each element, producing output like:\n\n```\ninfo: Sample.Program\n      => Main => TextFormattingExample\n      Hello, world!\n```\n\nWhen using `{#each <name> in <expr>}` over an object, such as the built-in `@p` (properties) object, `<name>` will be bound to the _names_ of the properties of the object.\n\nTo get to the _values_ of the properties, use a second binding:\n\n```\n{#each k, v in @p}{k} = {v}{#delimit},{#end}\n```\n\nThis example, if an event has three properties, will produce output like:\n\n```\nAccount = \"nblumhardt\", Cart = [\"Tea\", \"Coffee\"], Powerup = 42\n```\n\nThe syntax supports `{#each <name>[, <name>] in <expr>}`, an optional `{#delimit}` block, and finally an optional `{#else}` block, which will be evaluated if the array or object is empty.\n\n## Recipes\n\n**Trim down `SourceContext` to a type name only:**\n\n```\nSubstring(SourceContext, LastIndexOf(SourceContext, '.') + 1)\n```\n\nThis expression takes advantage of `LastIndexOf()` returning -1 when no `.` character appears in `SourceContext`, to yield a `startIndex` of 0 in that case.\n\n**Write not-referenced context properties (only if there are any):**\n\n```\n{#if rest(true) <> {}} <Context: {rest(true)}>{#end}\n```\n\n**Access a property with a non-identifier name:**\n\n```\n@p['some name']\n```\n\nAny structured value, including the built-in `@p`, can be indexed by string key. This means that `User.Name` and `User['Name']` are equivalent, for example.\n\n**Access a property with inconsistent casing:**\n\n```\nElementAt(@p, 'someName') ci\n```\n\n`ElementAt()` is a function-call version of the `[]` indexer notation, which means it can accept the `ci` case-insensitivity modifier.\n\n**Format events as newline-delimited JSON (template, embedded in C# or JSON):**\n\n```\n{ {Timestamp: @t, Username: User.Name} }\\n\n```\n\nThis output template shows the use of a space between the opening `{` of a hole, and the enclosed object literal with `Timestamp` and\n`Username` fields. The object will be formatted as JSON. The trailing `\\n` is a C# or JSON newline literal (don't escape this any further, as\nit's not part of the output template syntax).\n\n## Working with the raw API\n\nThe package provides the class `SerilogExpression` in the `Serilog.Expressions` namespace\nfor working with expressions.\n\n```csharp\nif (SerilogExpression.TryCompile(\"RequestPath like '/health%'\", out var compiled, out var error)\n{\n    // `compiled` is a function that can be executed against `LogEvent`s:\n    var result = compiled(someEvent);\n\n    // `result` will contain a `LogEventPropertyValue`, or `null` if the result of evaluating the\n    // expression is undefined (for example if the event has no `RequestPath` property).\n    if (result is ScalarValue value &&\n        value.Value is bool matches &&\n        matches)\n    {\n        Console.WriteLine(\"The event matched.\");\n    }\n}\nelse\n{\n    // `error` describes a syntax error.\n    Console.WriteLine($\"Couldn't compile the expression; {error}.\");\n}\n```\n\nCompiled expression delegates return `LogEventPropertyValue` because this is the most\nconvenient type to work with in many Serilog scenarios (enrichers, sinks, ...). To\nconvert the result to plain-old-.NET-types like `string`, `bool`, `Dictionary<K,V>` and\n`Array`, use the functions in the `Serilog.Expressions.ExpressionResult` class:\n\n```csharp\n    var result = compiled(someEvent);\n\n    // `true` only if `result` is a scalar Boolean `true`; `false` otherwise:\n    if (ExpressionResult.IsTrue(result))\n    {\n        Console.WriteLine(\"The event matched.\");\n    }\n```\n\n## Implementing user-defined functions\n\nUser-defined functions can be plugged in by implementing static methods that:\n\n * Return `LogEventPropertyValue?`,\n * Have arguments of type `LogEventPropertyValue?` or `LogEvent`,\n * If the `ci` modifier is supported, accept a `StringComparison`, and\n * If culture-specific formatting or comparisons are used, accepts an `IFormatProvider`.\n\nFor example:\n\n```csharp\npublic static class MyFunctions\n{\n    public static LogEventPropertyValue? IsHello(\n        StringComparison comparison,\n        LogEventPropertyValue? maybeHello)\n    {\n        if (maybeHello is ScalarValue sv && sv.Value is string s)\n            return new ScalarValue(s.Equals(\"Hello\", comparison));\n\n        // Undefined - argument was not a string.\n        return null;\n    }\n}\n```\n\nIn the example, `IsHello('Hello')` will evaluate to `true`, `IsHello('HELLO')` will be `false`, `IsHello('HELLO') ci`\nwill be `true`, and `IsHello(42)` will be undefined.\n\nUser-defined functions are supplied through an instance of `NameResolver`:\n\n```csharp\nvar myFunctions = new StaticMemberNameResolver(typeof(MyFunctions));\nvar expr = SerilogExpression.Compile(\"IsHello(User.Name)\", nameResolver: myFunctions);\n// Filter events based on whether `User.Name` is `'Hello'` :-)\n```\n\n## Acknowledgements\n\nIncludes the parser combinator implementation from [Superpower](https://github.com/datalust/superpower), copyright Datalust,\nSuperpower Contributors, and Sprache Contributors; licensed under the Apache License, 2.0.\n"
  },
  {
    "path": "RunPerfTests.ps1",
    "content": "Push-Location $PSScriptRoot\n\n./Build.ps1\n\nforeach ($test in ls test/*.PerformanceTests) {\n    Push-Location $test\n\n\techo \"perf: Running performance test project in $test\"\n\n    & dotnet test -c Release\n    if($LASTEXITCODE -ne 0) { exit 2 }\n\n    Pop-Location\n}\n\nPop-Location\n"
  },
  {
    "path": "example/Sample/Program.cs",
    "content": "﻿using Serilog;\nusing Serilog.Debugging;\nusing Serilog.Templates;\nusing Serilog.Templates.Themes;\n\nSelfLog.Enable(Console.Error);\n\nTextFormattingExample1();\nJsonFormattingExample();\nPipelineComponentExample();\nTextFormattingExample2();\n\nreturn;\n\nstatic void TextFormattingExample1()\n{\n    using var log = new LoggerConfiguration()\n        .Enrich.WithProperty(\"Application\", \"Sample\")\n        .WriteTo.Console(new ExpressionTemplate(\n            \"[{@t:HH:mm:ss} {@l:u3}\" +\n            \"{#if SourceContext is not null} ({Substring(SourceContext, LastIndexOf(SourceContext, '.') + 1)}){#end}] \" +\n            \"{@m} (first item is {coalesce(Items[0], '<empty>')}) {rest()}\\n{@x}\",\n            theme: TemplateTheme.Code))\n        .CreateLogger();\n\n    log.Information(\"Running {Example}\", nameof(TextFormattingExample1));\n\n    log.ForContext<Program>()\n        .Information(\"Cart contains {@Items}\", new[] { \"Tea\", \"Coffee\" });\n\n    log.ForContext<Program>()\n        .Information(\"Cart contains {@Items}\", new[] { \"Apricots\" });\n}\n\nstatic void JsonFormattingExample()\n{\n    using var log = new LoggerConfiguration()\n        .Enrich.WithProperty(\"Application\", \"Example\")\n        .WriteTo.Console(new ExpressionTemplate(\n            \"{ {@t: UtcDateTime(@t), @mt, @l: if @l = 'Information' then undefined() else @l, @x, ..@p} }\\n\"))\n        .CreateLogger();\n\n    log.Information(\"Running {Example}\", nameof(JsonFormattingExample));\n\n    log.ForContext<Program>()\n        .Information(\"Cart contains {@Items}\", new[] { \"Tea\", \"Coffee\" });\n\n    log.ForContext<Program>()\n        .Warning(\"Cart is empty\");\n}\n\nstatic void PipelineComponentExample()\n{\n    using var log = new LoggerConfiguration()\n        .Enrich.WithProperty(\"Application\", \"Example\")\n        .Enrich.WithComputed(\"FirstItem\", \"coalesce(Items[0], '<empty>')\")\n        .Enrich.WithComputed(\"SourceContext\", \"coalesce(Substring(SourceContext, LastIndexOf(SourceContext, '.') + 1), '<no source>')\")\n        .Filter.ByIncludingOnly(\"Items is null or Items[?] like 'C%'\")\n        .WriteTo.Console(outputTemplate:\n            \"[{Timestamp:HH:mm:ss} {Level:u3} ({SourceContext})] {Message:lj} (first item is {FirstItem}){NewLine}{Exception}\")\n        .CreateLogger();\n\n    log.Information(\"Running {Example}\", nameof(PipelineComponentExample));\n\n    log.ForContext<Program>()\n        .Information(\"Cart contains {@Items}\", new[] { \"Tea\", \"Coffee\" });\n\n    log.ForContext<Program>()\n        .Information(\"Cart contains {@Items}\", new[] { \"Apricots\" });\n}\n\nstatic void TextFormattingExample2()\n{\n    // Emulates `Microsoft.Extensions.Logging`'s `ConsoleLogger`.\n\n    var melon = new TemplateTheme(TemplateTheme.Literate, new Dictionary<TemplateThemeStyle, string>\n    {\n        // `Information` is dark green in MEL.\n        [TemplateThemeStyle.LevelInformation] = \"\\x1b[38;5;34m\",\n        [TemplateThemeStyle.String] = \"\\x1b[38;5;159m\",\n        [TemplateThemeStyle.Number] = \"\\x1b[38;5;159m\"\n    });\n\n    using var log = new LoggerConfiguration()\n        .WriteTo.Console(new ExpressionTemplate(\n            \"{@l:w4}: {SourceContext}\\n\" +\n            \"{#if Scope is not null}\" +\n            \"      {#each s in Scope}=> {s}{#delimit} {#end}\\n\" +\n            \"{#end}\" +\n            \"      {@m}\\n\" +\n            \"{@x}\",\n            theme: melon))\n        .CreateLogger();\n\n    var program = log.ForContext<Program>();\n    program.Information(\"Host listening at {ListenUri}\", \"https://hello-world.local\");\n\n    program\n        .ForContext(\"Scope\", new[] {\"Main\", \"TextFormattingExample2()\"})\n        .Information(\"HTTP {Method} {Path} responded {StatusCode} in {Elapsed:0.000} ms\", \"GET\", \"/api/hello\", 200, 1.23);\n\n    program.Warning(\"We've reached the end of the line\");\n}\n"
  },
  {
    "path": "example/Sample/Sample.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>net9.0</TargetFramework>\n    <OutputType>Exe</OutputType>\n    <GenerateDocumentationFile>false</GenerateDocumentationFile>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Serilog.Sinks.Console\" Version=\"6.0.0\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"../../src/Serilog.Expressions/Serilog.Expressions.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "global.json",
    "content": "{\n  \"sdk\": {\n    \"version\": \"9.0.200\",\n    \"allowPrerelease\": false,\n    \"rollForward\": \"latestFeature\"\n  }\n}\n"
  },
  {
    "path": "serilog-expressions.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 16\nVisualStudioVersion = 16.0.31410.223\nMinimumVisualStudioVersion = 10.0.40219.1\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"src\", \"src\", \"{91E482DE-E1E7-4CE1-9511-C0AF07F3648A}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"global\", \"global\", \"{24B1126F-184C-469E-9F06-C1019DEF165A}\"\n\tProjectSection(SolutionItems) = preProject\n\t\t.gitattributes = .gitattributes\n\t\t.gitignore = .gitignore\n\t\tBuild.ps1 = Build.ps1\n\t\tDirectory.Build.props = Directory.Build.props\n\t\tLICENSE = LICENSE\n\t\tREADME.md = README.md\n\t\tRunPerfTests.ps1 = RunPerfTests.ps1\n\t\tDirectory.Version.props = Directory.Version.props\n\t\tglobal.json = global.json\n\tEndProjectSection\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"example\", \"example\", \"{BD94A77E-34B1-478E-B921-E87A5F71B574}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"test\", \"test\", \"{B03B3086-D197-4B32-9AE2-8536C345EA2D}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"Sample\", \"example\\Sample\\Sample.csproj\", \"{776EECAC-3C50-45EA-847D-0EBE5158E51E}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"Serilog.Expressions\", \"src\\Serilog.Expressions\\Serilog.Expressions.csproj\", \"{A420C4E3-3A2D-4369-88EB-77E4DB1D0219}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"Serilog.Expressions.Tests\", \"test\\Serilog.Expressions.Tests\\Serilog.Expressions.Tests.csproj\", \"{3C2D8E01-5580-426A-BDD9-EC59CD98E618}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"Serilog.Expressions.PerformanceTests\", \"test\\Serilog.Expressions.PerformanceTests\\Serilog.Expressions.PerformanceTests.csproj\", \"{D7A37F73-BBA3-4DAE-9648-1A753A86F968}\"\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tDebug|Any CPU = Debug|Any CPU\n\t\tRelease|Any CPU = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{776EECAC-3C50-45EA-847D-0EBE5158E51E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{776EECAC-3C50-45EA-847D-0EBE5158E51E}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{776EECAC-3C50-45EA-847D-0EBE5158E51E}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{776EECAC-3C50-45EA-847D-0EBE5158E51E}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{A420C4E3-3A2D-4369-88EB-77E4DB1D0219}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{A420C4E3-3A2D-4369-88EB-77E4DB1D0219}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{A420C4E3-3A2D-4369-88EB-77E4DB1D0219}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{A420C4E3-3A2D-4369-88EB-77E4DB1D0219}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{3C2D8E01-5580-426A-BDD9-EC59CD98E618}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{3C2D8E01-5580-426A-BDD9-EC59CD98E618}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{3C2D8E01-5580-426A-BDD9-EC59CD98E618}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{3C2D8E01-5580-426A-BDD9-EC59CD98E618}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{D7A37F73-BBA3-4DAE-9648-1A753A86F968}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{D7A37F73-BBA3-4DAE-9648-1A753A86F968}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{D7A37F73-BBA3-4DAE-9648-1A753A86F968}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{D7A37F73-BBA3-4DAE-9648-1A753A86F968}.Release|Any CPU.Build.0 = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\n\tGlobalSection(NestedProjects) = preSolution\n\t\t{776EECAC-3C50-45EA-847D-0EBE5158E51E} = {BD94A77E-34B1-478E-B921-E87A5F71B574}\n\t\t{A420C4E3-3A2D-4369-88EB-77E4DB1D0219} = {91E482DE-E1E7-4CE1-9511-C0AF07F3648A}\n\t\t{3C2D8E01-5580-426A-BDD9-EC59CD98E618} = {B03B3086-D197-4B32-9AE2-8536C345EA2D}\n\t\t{D7A37F73-BBA3-4DAE-9648-1A753A86F968} = {B03B3086-D197-4B32-9AE2-8536C345EA2D}\n\tEndGlobalSection\n\tGlobalSection(ExtensibilityGlobals) = postSolution\n\t\tSolutionGuid = {EB6672D6-318E-493E-8B60-77F5A7A90E66}\n\tEndGlobalSection\nEndGlobal\n"
  },
  {
    "path": "serilog-expressions.sln.DotSettings",
    "content": "﻿<wpf:ResourceDictionary xml:space=\"preserve\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" xmlns:s=\"clr-namespace:System;assembly=mscorlib\" xmlns:ss=\"urn:shemas-jetbrains-com:settings-storage-xaml\" xmlns:wpf=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n\t<s:String x:Key=\"/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=CI/@EntryIndexedValue\">CI</s:String>\n\t<s:Boolean x:Key=\"/Default/UserDictionary/Words/=Acerola/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/UserDictionary/Words/=Comparand/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/UserDictionary/Words/=delim/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/UserDictionary/Words/=Enricher/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/UserDictionary/Words/=Evaluatable/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/UserDictionary/Words/=Existentials/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/UserDictionary/Words/=formattable/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/UserDictionary/Words/=nblumhardt/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/UserDictionary/Words/=ogham/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/UserDictionary/Words/=Reorderable/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/UserDictionary/Words/=Serilog/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/UserDictionary/Words/=Subproperties/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/UserDictionary/Words/=Variadics/@EntryIndexedValue\">True</s:Boolean></wpf:ResourceDictionary>"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Ast/AccessorExpression.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nnamespace Serilog.Expressions.Ast;\n\n/// <summary>\n/// An accessor retrieves a property from a (structured) object. For example, in the expression\n/// <code>Headers.ContentType</code> the <code>.</code> operator forms an accessor expression that\n/// retrieves the <code>ContentType</code> property from the <code>Headers</code> object.\n/// </summary>\n/// <remarks>Note that the AST type can represent accessors that cannot be validly written using\n/// <code>.</code> notation. In these cases, if the accessor is formatted back out as an expression,\n/// <see cref=\"IndexerExpression\"/> notation will be used.</remarks>\nclass AccessorExpression : Expression\n{\n    public AccessorExpression(Expression receiver, string memberName)\n    {\n        MemberName = memberName ?? throw new ArgumentNullException(nameof(memberName));\n        Receiver = receiver;\n    }\n\n    public string MemberName { get; }\n\n    public Expression Receiver { get; }\n\n    public override string ToString()\n    {\n        if (SerilogExpression.IsValidIdentifier(MemberName))\n            return Receiver + \".\" + MemberName;\n\n        return $\"{Receiver}['{SerilogExpression.EscapeStringContent(MemberName)}']\";\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Ast/AmbientNameExpression.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nnamespace Serilog.Expressions.Ast;\n\n/// <summary>\n/// An ambient name is generally a property name or built-in that appears standalone in an expression. For example,\n/// in <code>Headers.ContentType</code>, <code>Headers</code> is an ambient name that produces an\n/// <see cref=\"AmbientNameExpression\"/>. Built-ins like <code>@Level</code> are also parsed as ambient names.\n/// </summary>\nclass AmbientNameExpression : Expression\n{\n    readonly bool _requiresEscape;\n\n    public AmbientNameExpression(string name, bool isBuiltIn)\n    {\n        PropertyName = name ?? throw new ArgumentNullException(nameof(name));\n        IsBuiltIn = isBuiltIn;\n        _requiresEscape = !SerilogExpression.IsValidIdentifier(name);\n    }\n\n    public string PropertyName { get; }\n\n    public bool IsBuiltIn { get; }\n\n    public override string ToString()\n    {\n        if (_requiresEscape)\n            return $\"@Properties['{SerilogExpression.EscapeStringContent(PropertyName)}']\";\n\n        return (IsBuiltIn ? \"@\" : \"\") + PropertyName;\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Ast/ArrayExpression.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nnamespace Serilog.Expressions.Ast;\n\n/// <summary>\n/// An array expression constructs an array from a list of elements. For example, <code>[1, 2, 3]</code> is an\n/// array expression. The items in an array expression can be literal values or expressions, like in the\n/// above example, or they can be spread expressions that describe zero or more elements to include in the\n/// list. Whether included via regular elements or spread expressions, undefined values are ignored and won't\n/// appear in the resulting array value.\n/// </summary>\nclass ArrayExpression : Expression\n{\n    public ArrayExpression(Element[] elements)\n    {\n        Elements = elements ?? throw new ArgumentNullException(nameof(elements));\n    }\n\n    public Element[] Elements { get; }\n\n    public override string ToString()\n    {\n        return \"[\" + string.Join(\", \", Elements.Select(o => o.ToString())) + \"]\";\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Ast/CallExpression.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nnamespace Serilog.Expressions.Ast;\n\n/// <summary>\n/// A <see cref=\"CallExpression\"/> is a function call made up of the function name, parenthesised argument\n/// list, and optional postfix <code>ci</code> modifier. For example, <code>Substring(RequestPath, 0, 5)</code>.\n/// </summary>\nclass CallExpression : Expression\n{\n    public CallExpression(bool ignoreCase, string operatorName, params Expression[] operands)\n    {\n        IgnoreCase = ignoreCase;\n        OperatorName = operatorName ?? throw new ArgumentNullException(nameof(operatorName));\n        Operands = operands ?? throw new ArgumentNullException(nameof(operands));\n    }\n\n    public bool IgnoreCase { get; }\n\n    public string OperatorName { get; }\n\n    public Expression[] Operands { get; }\n\n    public override string ToString()\n    {\n        return OperatorName\n               + \"(\" + string.Join(\", \", Operands.Select(o => o.ToString())) + \")\"\n               + (IgnoreCase ? \" ci\" : \"\");\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Ast/ConstantExpression.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing System.Globalization;\nusing Serilog.Events;\n\nnamespace Serilog.Expressions.Ast;\n\n/// <summary>\n/// A constant such as <code>'hello'</code>, <code>true</code>, <code>null</code>, or <code>123.45</code>.\n/// </summary>\nclass ConstantExpression : Expression\n{\n    public ConstantExpression(LogEventPropertyValue constant)\n    {\n        Constant = constant ?? throw new ArgumentNullException(nameof(constant));\n    }\n\n    public LogEventPropertyValue Constant { get; }\n\n    public override string ToString()\n    {\n        if (Constant is ScalarValue sv)\n        {\n            return sv.Value switch\n            {\n                string s => \"'\" + s.Replace(\"'\", \"''\") + \"'\",\n                true => \"true\",\n                false => \"false\",\n                IFormattable formattable => formattable.ToString(null, CultureInfo.InvariantCulture),\n                _ => (sv.Value ?? \"null\").ToString() ?? \"<ToString() returned null>\"\n            };\n        }\n\n        return Constant.ToString();\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Ast/Element.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nnamespace Serilog.Expressions.Ast;\n\n/// <summary>\n/// An element in an <see cref=\"ArrayExpression\"/>.\n/// </summary>\nabstract class Element;"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Ast/Expression.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nnamespace Serilog.Expressions.Ast;\n\n/// <summary>\n/// An AST node.\n/// </summary>\nabstract class Expression\n{\n    /// <summary>\n    /// The <see cref=\"ToString\"/> representation of an <see cref=\"Expression\"/> is <strong>not</strong>\n    /// guaranteed to be syntactically valid: this is provided for debugging purposes only.\n    /// </summary>\n    /// <returns>A textual representation of the expression.</returns>\n    public abstract override string ToString();\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Ast/IndexOfMatchExpression.cs",
    "content": "// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing System.Text.RegularExpressions;\n\nnamespace Serilog.Expressions.Ast;\n\n/// <summary>\n/// A non-syntax expression tree node used when compiling the <see cref=\"Operators.OpIndexOfMatch\"/>,\n/// <see cref=\"Operators.OpIsMatch\"/>, and SQL-style <code>like</code> expressions.\n/// </summary>\nclass IndexOfMatchExpression : Expression\n{\n    public Expression Corpus { get; }\n    public Regex Regex { get; }\n\n    public IndexOfMatchExpression(Expression corpus, Regex regex)\n    {\n        Corpus = corpus ?? throw new ArgumentNullException(nameof(corpus));\n        Regex = regex ?? throw new ArgumentNullException(nameof(regex));\n    }\n\n    public override string ToString()\n    {\n        return $\"_Internal_IndexOfMatch({Corpus}, '{Regex.ToString().Replace(\"'\", \"''\")}')\";\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Ast/IndexerExpression.cs",
    "content": "// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nnamespace Serilog.Expressions.Ast;\n\n/// <summary>\n/// An <see cref=\"IndexerExpression\"/> retrieves a property from an object, by name, or an item from an array\n/// by zero-based numeric index. For example, <code>Headers['Content-Type']</code> and <code>Items[2]</code> are\n/// parsed as indexer expressions.\n/// </summary>\nclass IndexerExpression : Expression\n{\n    public Expression Receiver { get; }\n    public Expression Index { get; }\n\n    public IndexerExpression(Expression receiver, Expression index)\n    {\n        Receiver = receiver;\n        Index = index;\n    }\n\n    public override string ToString()\n    {\n        return $\"{Receiver}[{Index}]\";\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Ast/IndexerWildcard.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nnamespace Serilog.Expressions.Ast;\n\n/// <summary>\n/// Describes the wildcard in a <see cref=\"IndexerWildcardExpression\"/>.\n/// </summary>\nenum IndexerWildcard { Undefined, Any, All }"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Ast/IndexerWildcardExpression.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nnamespace Serilog.Expressions.Ast;\n\n/// <summary>\n/// An indexer wildcard is a placeholder in a property path expression. For example,\n/// in <code>Headers[?] = 'test'</code>, the question-mark token is a wildcard that means \"any property of\n/// the <code>Headers</code> object\". The other wildcard indexer is the asterisk, meaning \"all\".\n/// </summary>\nclass IndexerWildcardExpression : Expression\n{\n    public IndexerWildcardExpression(IndexerWildcard wildcard)\n    {\n        Wildcard = wildcard;\n    }\n\n    public IndexerWildcard Wildcard { get; }\n\n    public override string ToString()\n    {\n        switch (Wildcard)\n        {\n            case IndexerWildcard.Any:\n                return \"?\";\n            case IndexerWildcard.All:\n                return \"*\";\n            default:\n                throw new NotSupportedException(\"Unrecognized wildcard \" + Wildcard);\n        }\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Ast/ItemElement.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nnamespace Serilog.Expressions.Ast;\n\n/// <summary>\n/// A single item in an <see cref=\"ArrayExpression\"/>.\n/// </summary>\nclass ItemElement : Element\n{\n    public Expression Value { get; }\n\n    public ItemElement(Expression value)\n    {\n        Value = value;\n    }\n\n    public override string ToString()\n    {\n        return Value.ToString();\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Ast/LambdaExpression.cs",
    "content": "// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nnamespace Serilog.Expressions.Ast;\n\n/// <summary>\n/// A non-syntax expression tree node used in the compilation of <see cref=\"IndexerWildcardExpression\"/>. Only\n/// very limited support for lambda expressions is currently present.\n/// </summary>\nclass LambdaExpression : Expression\n{\n    public LambdaExpression(ParameterExpression[] parameters, Expression body)\n    {\n        Parameters = parameters;\n        Body = body;\n    }\n\n    public ParameterExpression[] Parameters { get; }\n\n    public Expression Body { get; }\n\n    public override string ToString()\n    {\n        return \"|\" + string.Join(\", \", Parameters.Select(p => p.ToString())) + \"| {\" + Body + \"}\";\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Ast/LocalNameExpression.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nnamespace Serilog.Expressions.Ast;\n\n/// <summary>\n/// Represents the iteration variable in template <code>#each</code> directives.\n/// </summary>\nclass LocalNameExpression : Expression\n{\n    public LocalNameExpression(string name)\n    {\n        Name = name ?? throw new ArgumentNullException(nameof(name));\n    }\n\n    public string Name { get; }\n\n    public override string ToString()\n    {\n        // No unambiguous syntax for this right now, `$` will do to make these stand out when debugging,\n        // but the result won't round-trip parse.\n        return $\"${Name}\";\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Ast/Member.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nnamespace Serilog.Expressions.Ast;\n\n/// <summary>\n/// A member in an <see cref=\"ObjectExpression\"/>.\n/// </summary>\nabstract class Member;"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Ast/ObjectExpression.cs",
    "content": "// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nnamespace Serilog.Expressions.Ast;\n\n/// <summary>\n/// Constructs an object given a list of members. Members can be <code>name: value</code> pairs, or spread\n/// expressions that include members from another object. Where names conflict, the rightmost appearance of\n/// a name wins. Members that evaluate to an undefined value do not appear in the resulting object.\n/// </summary>\nclass ObjectExpression : Expression\n{\n    public ObjectExpression(Member[] members)\n    {\n        Members = members;\n    }\n\n    public Member[] Members { get; }\n\n    public override string ToString()\n    {\n        return \"{\" + string.Join(\", \", Members.Select(m => m.ToString())) + \"}\";\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Ast/ParameterExpression.cs",
    "content": "// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nnamespace Serilog.Expressions.Ast;\n\n/// <summary>\n/// Non-syntax expression type used to represent parameters in <see cref=\"LambdaExpression\"/> bodies.\n/// </summary>\nclass ParameterExpression : Expression\n{\n    public ParameterExpression(string parameterName)\n    {\n        ParameterName = parameterName;\n    }\n\n    public string ParameterName { get; }\n\n    public override string ToString()\n    {\n        return \"$$\" + ParameterName;\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Ast/PropertyMember.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Events;\n\nnamespace Serilog.Expressions.Ast;\n\n/// <summary>\n/// An <see cref=\"ObjectExpression\"/> member comprising an optionally-quoted name and a value, for example\n/// the object <code>{Username: 'alice'}</code> includes a single <see cref=\"PropertyMember\"/> with name\n/// <code>Username</code> and value <code>'alice'</code>.\n/// </summary>\nclass PropertyMember : Member\n{\n    public string Name { get; }\n    public Expression Value { get; }\n\n    public PropertyMember(string name, Expression value)\n    {\n        Name = name;\n        Value = value;\n    }\n\n    public override string ToString()\n    {\n        return $\"{new ScalarValue(Name)}: {Value}\";\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Ast/SpreadElement.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nnamespace Serilog.Expressions.Ast;\n\n/// <summary>\n/// An element in an <see cref=\"ArrayExpression\"/> that describes zero or more items to include in the array.\n/// Spread elements are written with two dots preceding an expression that evaluates to an array of elements to\n/// insert into the result array at the position of the spread element, for example, in <code>[1, 2, ..Others]</code>,\n/// the <code>..Others</code> expression is a spread element. If the value of the array in the spread is\n/// undefined, no items will be added to the list.\n/// </summary>\nclass SpreadElement : Element\n{\n    public Expression Content { get; }\n\n    public SpreadElement(Expression content)\n    {\n        Content = content;\n    }\n\n    public override string ToString()\n    {\n        return $\"..{Content}\";\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Ast/SpreadMember.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nnamespace Serilog.Expressions.Ast;\n\n/// <summary>\n/// An <see cref=\"ObjectExpression\"/> member that designates another object from which to copy members into the\n/// current object. Spread member expressions comprise two dots preceding an expression that is expected to\n/// evaluate to an object.\n/// </summary>\nclass SpreadMember : Member\n{\n    public Expression Content { get; }\n\n    public SpreadMember(Expression content)\n    {\n        Content = content;\n    }\n\n    public override string ToString()\n    {\n        return $\"..{Content}\";\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/BuiltInProperty.cs",
    "content": "// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nnamespace Serilog.Expressions;\n\n// See https://github.com/serilog/serilog-formatting-compact#reified-properties\nstatic class BuiltInProperty\n{\n    public const string Exception = \"x\";\n    public const string Level = \"l\";\n    public const string Timestamp = \"t\";\n    public const string Message = \"m\";\n    public const string MessageTemplate = \"mt\";\n    public const string Properties = \"p\";\n    public const string Renderings = \"r\";\n    public const string EventId = \"i\";\n    public const string TraceId = \"tr\";\n    public const string SpanId = \"sp\";\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Compilation/Arrays/ConstantArrayEvaluator.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Events;\nusing Serilog.Expressions.Ast;\nusing Serilog.Expressions.Compilation.Transformations;\n\nnamespace Serilog.Expressions.Compilation.Arrays;\n\nclass ConstantArrayEvaluator : IdentityTransformer\n{\n    static readonly ConstantArrayEvaluator Instance = new();\n\n    public static Expression Rewrite(Expression expression)\n    {\n        return Instance.Transform(expression);\n    }\n\n    protected override Expression Transform(ArrayExpression ax)\n    {\n        // This should probably go depth-first.\n\n        if (ax.Elements.All(el => el is ItemElement item &&\n                                  item.Value is ConstantExpression))\n        {\n            return new ConstantExpression(\n                new SequenceValue(ax.Elements\n                    .Cast<ItemElement>()\n                    .Select(item => ((ConstantExpression)item.Value).Constant)));\n        }\n\n        return base.Transform(ax);\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Compilation/DefaultFunctionNameResolver.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Expressions.Runtime;\n\nnamespace Serilog.Expressions.Compilation;\n\nstatic class DefaultFunctionNameResolver\n{\n    public static NameResolver Build(NameResolver? additionalNameResolver)\n    {\n        var defaultResolver = new StaticMemberNameResolver(typeof(RuntimeOperators));\n        return additionalNameResolver == null\n            ? defaultResolver\n            : new OrderedNameResolver(new[] {defaultResolver, additionalNameResolver });\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Compilation/ExpressionCompiler.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Expressions.Ast;\nusing Serilog.Expressions.Compilation.Arrays;\nusing Serilog.Expressions.Compilation.Linq;\nusing Serilog.Expressions.Compilation.Properties;\nusing Serilog.Expressions.Compilation.Text;\nusing Serilog.Expressions.Compilation.Variadics;\nusing Serilog.Expressions.Compilation.Wildcards;\n\nnamespace Serilog.Expressions.Compilation;\n\nstatic class ExpressionCompiler\n{\n    public static Expression Translate(Expression expression)\n    {\n        var actual = expression;\n        actual = VariadicCallRewriter.Rewrite(actual);\n        actual = TextMatchingTransformer.Rewrite(actual);\n        actual = LikeSyntaxTransformer.Rewrite(actual);\n        actual = PropertiesObjectAccessorTransformer.Rewrite(actual);\n        actual = ConstantArrayEvaluator.Rewrite(actual);\n        actual = WildcardComprehensionTransformer.Rewrite(actual);\n        return actual;\n    }\n\n    public static Evaluatable Compile(Expression expression, IFormatProvider? formatProvider, NameResolver nameResolver)\n    {\n        var actual = Translate(expression);\n        return LinqExpressionCompiler.Compile(actual, formatProvider, nameResolver);\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Compilation/ExpressionValidationException.cs",
    "content": "// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nnamespace Serilog.Expressions.Compilation;\n\nclass ExpressionValidationException : ArgumentException\n{\n    public ExpressionValidationException(string message) : base(message)\n    {\n    }\n\n    public ExpressionValidationException(string message, Exception innerException) : base(message, innerException)\n    {\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Compilation/Linq/EventIdHash.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// ReSharper disable ForCanBeConvertedToForeach\n\nnamespace Serilog.Expressions.Compilation.Linq;\n\n/// <summary>\n/// Hash functions for message templates. See <see cref=\"Compute\"/>.\n/// </summary>\npublic static class EventIdHash\n{\n    /// <summary>\n    /// Compute a 32-bit hash of the provided <paramref name=\"messageTemplate\"/>. The\n    /// resulting hash value can be uses as an event id in lieu of transmitting the\n    /// full template string.\n    /// </summary>\n    /// <param name=\"messageTemplate\">A message template.</param>\n    /// <returns>A 32-bit hash of the template.</returns>\n    [CLSCompliant(false)]\n    public static uint Compute(string messageTemplate)\n    {\n        if (messageTemplate == null) throw new ArgumentNullException(nameof(messageTemplate));\n\n        // Jenkins one-at-a-time https://en.wikipedia.org/wiki/Jenkins_hash_function\n        unchecked\n        {\n            uint hash = 0;\n            for (var i = 0; i < messageTemplate.Length; ++i)\n            {\n                hash += messageTemplate[i];\n                hash += hash << 10;\n                hash ^= hash >> 6;\n            }\n            hash += hash << 3;\n            hash ^= hash >> 11;\n            hash += hash << 15;\n            return hash;\n        }\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Compilation/Linq/ExpressionConstantMapper.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing System.Linq.Expressions;\nusing Serilog.Events;\n\nnamespace Serilog.Expressions.Compilation.Linq;\n\nclass ExpressionConstantMapper : ExpressionVisitor\n{\n    readonly IDictionary<object, Expression> _mapping;\n\n    public ExpressionConstantMapper(IDictionary<object, Expression> mapping)\n    {\n        _mapping = mapping;\n    }\n\n    protected override Expression VisitConstant(ConstantExpression node)\n    {\n        if (node.Value is ScalarValue { Value: {} sv } &&\n            _mapping.TryGetValue(sv, out var substitute))\n            return substitute;\n\n        return base.VisitConstant(node);\n    }\n}\n"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Compilation/Linq/Intrinsics.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing System.Collections.Specialized;\nusing System.Text.RegularExpressions;\nusing Serilog.Events;\nusing Serilog.Expressions.Runtime;\nusing Serilog.Parsing;\nusing Serilog.Templates.Compilation;\n\n// ReSharper disable ParameterTypeCanBeEnumerable.Global\n\nnamespace Serilog.Expressions.Compilation.Linq;\n\nstatic class Intrinsics\n{\n    static readonly LogEventPropertyValue NegativeOne = new ScalarValue(-1);\n    static readonly LogEventPropertyValue Tombstone = new ScalarValue(\"😬 (if you see this you have found a bug.)\");\n\n    public static List<LogEventPropertyValue?> CollectSequenceElements(LogEventPropertyValue?[] elements)\n    {\n        return elements.ToList();\n    }\n\n    public static List<LogEventPropertyValue?> ExtendSequenceValueWithItem(List<LogEventPropertyValue?> elements,\n        LogEventPropertyValue? element)\n    {\n        // Mutates the list; returned so we can nest calls instead of emitting a block.\n        if (element != null)\n            elements.Add(element);\n        return elements;\n    }\n\n    public static List<LogEventPropertyValue?> ExtendSequenceValueWithSpread(List<LogEventPropertyValue?> elements,\n        LogEventPropertyValue? content)\n    {\n        if (content is SequenceValue sequence)\n            foreach (var element in sequence.Elements)\n                elements.Add(element);\n\n        return elements;\n    }\n\n    public static LogEventPropertyValue ConstructSequenceValue(List<LogEventPropertyValue?> elements)\n    {\n        if (elements.Any(el => el == null))\n            return new SequenceValue(elements.Where(el => el != null)!);\n\n        return new SequenceValue(elements!);\n    }\n\n    public static List<LogEventProperty> CollectStructureProperties(string[] names, LogEventPropertyValue?[] values)\n    {\n        var properties = new List<LogEventProperty>();\n        for (var i = 0; i < names.Length; ++i)\n        {\n            var name = names[i];\n            var value = values[i];\n            properties.Add(new(name, value ?? Tombstone));\n        }\n\n        return properties;\n    }\n\n    public static LogEventPropertyValue ConstructStructureValue(List<LogEventProperty> properties)\n    {\n        if (properties.Any(p => p == null || p.Value == Tombstone))\n            return new StructureValue(properties.Where(p => p != null && p.Value != Tombstone));\n\n        return new StructureValue(properties);\n    }\n\n    public static List<LogEventProperty> ExtendStructureValueWithSpread(\n        List<LogEventProperty> properties,\n        LogEventPropertyValue? content)\n    {\n        if (content is StructureValue structure)\n        {\n            foreach (var property in structure.Properties)\n                if (property != null)\n                    properties.Add(property);\n        }\n\n        return properties;\n    }\n\n    public static List<LogEventProperty> ExtendStructureValueWithProperty(\n        List<LogEventProperty> properties,\n        string name,\n        LogEventPropertyValue? value)\n    {\n        // Mutates the list; returned so we can nest calls instead of emitting a block.\n        properties.Add(new(name, value ?? Tombstone));\n        return properties;\n    }\n\n    public static LogEventPropertyValue CompleteStructureValue(List<LogEventProperty> properties)\n    {\n        var result = new OrderedDictionary();\n        foreach (var property in properties)\n        {\n            if (result.Contains(property.Name))\n                result.Remove(property.Name);\n            if (property.Value != Tombstone)\n                result.Add(property.Name, new LogEventProperty(property.Name, property.Value));\n        }\n        return new StructureValue(result.Values.Cast<LogEventProperty>().ToList());\n    }\n\n    public static bool CoerceToScalarBoolean(LogEventPropertyValue value)\n    {\n        if (value is ScalarValue sv && sv.Value is bool b)\n            return b;\n        return false;\n    }\n\n    public static LogEventPropertyValue? IndexOfMatch(LogEventPropertyValue value, Regex regex)\n    {\n        if (value is ScalarValue scalar &&\n            scalar.Value is string s)\n        {\n            var m = regex.Match(s);\n            if (m.Success)\n                return new ScalarValue(m.Index);\n            return NegativeOne;\n        }\n\n        return null;\n    }\n\n    public static LogEventPropertyValue? GetPropertyValue(EvaluationContext ctx, string propertyName)\n    {\n        if (!ctx.LogEvent.Properties.TryGetValue(propertyName, out var value))\n            return null;\n\n        return value;\n    }\n\n    public static LogEventPropertyValue? GetLocalValue(EvaluationContext ctx, string localName)\n    {\n        if (!Locals.TryGetValue(ctx.Locals, localName, out var value))\n            return null;\n\n        return value;\n    }\n\n    public static LogEventPropertyValue? TryGetStructurePropertyValue(StringComparison sc, LogEventPropertyValue maybeStructure, string name)\n    {\n        if (maybeStructure is StructureValue sv)\n        {\n            foreach (var prop in sv.Properties)\n            {\n                if (prop.Name.Equals(name, sc))\n                {\n                    return prop.Value;\n                }\n            }\n        }\n\n        return null;\n    }\n\n    // Use of `CompiledMessageToken` is a layering violation here, but we want to ensure the formatting implementations\n    // line up exactly. Some refactoring here might be worthwhile, though with an eye on indirection costs.\n    public static string RenderMessage(CompiledMessageToken formatter, EvaluationContext ctx)\n    {\n        var sw = new StringWriter();\n        formatter.Evaluate(ctx, sw);\n        return sw.ToString();\n    }\n\n    public static LogEventPropertyValue? GetRenderings(LogEvent logEvent, IFormatProvider? formatProvider)\n    {\n        List<LogEventPropertyValue>? elements = null;\n        foreach (var token in logEvent.MessageTemplate.Tokens)\n        {\n            if (token is PropertyToken {Format: { }} pt)\n            {\n                elements ??= new();\n\n                var space = new StringWriter();\n\n                pt.Render(logEvent.Properties, space, formatProvider);\n                elements.Add(new ScalarValue(space.ToString()));\n            }\n        }\n\n        return elements == null ? null : new SequenceValue(elements);\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Compilation/Linq/LinqExpressionCompiler.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing System.Linq.Expressions;\nusing System.Reflection;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing Serilog.Debugging;\nusing Serilog.Events;\nusing Serilog.Expressions.Ast;\nusing Serilog.Expressions.Compilation.Transformations;\nusing Serilog.Templates.Compilation;\nusing Serilog.Templates.Themes;\nusing ConstantExpression = Serilog.Expressions.Ast.ConstantExpression;\nusing Expression = Serilog.Expressions.Ast.Expression;\nusing ParameterExpression = System.Linq.Expressions.ParameterExpression;\nusing LX = System.Linq.Expressions.Expression;\nusing ExpressionBody = System.Linq.Expressions.Expression;\n// ReSharper disable UseIndexFromEndExpression\n\nnamespace Serilog.Expressions.Compilation.Linq;\n\nclass LinqExpressionCompiler : SerilogExpressionTransformer<ExpressionBody>\n{\n    readonly NameResolver _nameResolver;\n    readonly IFormatProvider? _formatProvider;\n\n    static readonly MethodInfo CollectSequenceElementsMethod = typeof(Intrinsics)\n        .GetMethod(nameof(Intrinsics.CollectSequenceElements), BindingFlags.Static | BindingFlags.Public)!;\n\n    static readonly MethodInfo ExtendSequenceValueWithSpreadMethod = typeof(Intrinsics)\n        .GetMethod(nameof(Intrinsics.ExtendSequenceValueWithSpread), BindingFlags.Static | BindingFlags.Public)!;\n\n    static readonly MethodInfo ExtendSequenceValueWithItemMethod = typeof(Intrinsics)\n        .GetMethod(nameof(Intrinsics.ExtendSequenceValueWithItem), BindingFlags.Static | BindingFlags.Public)!;\n\n    static readonly MethodInfo ConstructSequenceValueMethod = typeof(Intrinsics)\n        .GetMethod(nameof(Intrinsics.ConstructSequenceValue), BindingFlags.Static | BindingFlags.Public)!;\n\n    static readonly MethodInfo CollectStructurePropertiesMethod = typeof(Intrinsics)\n        .GetMethod(nameof(Intrinsics.CollectStructureProperties), BindingFlags.Static | BindingFlags.Public)!;\n\n    static readonly MethodInfo ConstructStructureValueMethod = typeof(Intrinsics)\n        .GetMethod(nameof(Intrinsics.ConstructStructureValue), BindingFlags.Static | BindingFlags.Public)!;\n\n    static readonly MethodInfo CompleteStructureValueMethod = typeof(Intrinsics)\n        .GetMethod(nameof(Intrinsics.CompleteStructureValue), BindingFlags.Static | BindingFlags.Public)!;\n\n    static readonly MethodInfo ExtendStructureValueWithSpreadMethod = typeof(Intrinsics)\n        .GetMethod(nameof(Intrinsics.ExtendStructureValueWithSpread), BindingFlags.Static | BindingFlags.Public)!;\n\n    static readonly MethodInfo ExtendStructureValueWithPropertyMethod = typeof(Intrinsics)\n        .GetMethod(nameof(Intrinsics.ExtendStructureValueWithProperty), BindingFlags.Static | BindingFlags.Public)!;\n\n    static readonly MethodInfo CoerceToScalarBooleanMethod = typeof(Intrinsics)\n        .GetMethod(nameof(Intrinsics.CoerceToScalarBoolean), BindingFlags.Static | BindingFlags.Public)!;\n\n    static readonly MethodInfo IndexOfMatchMethod = typeof(Intrinsics)\n        .GetMethod(nameof(Intrinsics.IndexOfMatch), BindingFlags.Static | BindingFlags.Public)!;\n\n    static readonly MethodInfo TryGetStructurePropertyValueMethod = typeof(Intrinsics)\n        .GetMethod(nameof(Intrinsics.TryGetStructurePropertyValue), BindingFlags.Static | BindingFlags.Public)!;\n\n    static readonly PropertyInfo EvaluationContextLogEventProperty = typeof(EvaluationContext)\n        .GetProperty(nameof(EvaluationContext.LogEvent), BindingFlags.Instance | BindingFlags.Public)!;\n\n    ParameterExpression Context { get; } = LX.Variable(typeof(EvaluationContext), \"ctx\");\n\n    LinqExpressionCompiler(IFormatProvider? formatProvider, NameResolver nameResolver)\n    {\n        _nameResolver = nameResolver;\n        _formatProvider = formatProvider;\n    }\n\n    public static Evaluatable Compile(Expression expression, IFormatProvider? formatProvider,\n        NameResolver nameResolver)\n    {\n        if (expression == null) throw new ArgumentNullException(nameof(expression));\n        var compiler = new LinqExpressionCompiler(formatProvider, nameResolver);\n        var body = compiler.Transform(expression);\n        return LX.Lambda<Evaluatable>(body, compiler.Context).Compile();\n    }\n\n    ExpressionBody Splice(Expression<Evaluatable> lambda)\n    {\n        return ParameterReplacementVisitor.ReplaceParameters(lambda, Context);\n    }\n\n    protected override ExpressionBody Transform(CallExpression call)\n    {\n        if (!_nameResolver.TryResolveFunctionName(call.OperatorName, out var m))\n            throw new ExpressionValidationException($\"The function name `{call.OperatorName}` was not recognized.\");\n\n        var methodParameters = m.GetParameters()\n            .Select(info => (pi: info, optional: info.GetCustomAttribute<OptionalAttribute>() != null))\n            .ToList();\n\n        // Log warning for CI modifier usage on functions that don't support it\n        // Note: We log a warning rather than throwing to maintain backward compatibility\n        // Previously, invalid CI usage was silently ignored\n        if (call.IgnoreCase)\n        {\n            var supportsStringComparison = methodParameters.Any(p => p.pi.ParameterType == typeof(StringComparison));\n            if (!supportsStringComparison)\n            {\n                SelfLog.WriteLine($\"The function `{call.OperatorName}` does not support case-insensitive operation; the 'ci' modifier will be ignored.\");\n            }\n        }\n\n        var allowedParameters = methodParameters.Where(info => info.pi.ParameterType == typeof(LogEventPropertyValue)).ToList();\n        var requiredParameterCount = allowedParameters.Count(info => !info.optional);\n\n        if (call.Operands.Length < requiredParameterCount || call.Operands.Length > allowedParameters.Count)\n        {\n            var requirements = DescribeRequirements(allowedParameters.Select(info => (info.pi.Name!, info.optional)).ToList());\n            throw new ArgumentException($\"The function `{call.OperatorName}` {requirements}.\");\n        }\n\n        var operands = new Queue<LX>(call.Operands.Select(Transform));\n\n        // `and` and `or` short-circuit to save execution time; unlike the earlier Serilog.Filters.Expressions, nothing else does.\n        if (Operators.SameOperator(call.OperatorName, Operators.RuntimeOpAnd))\n            return CompileLogical(LX.AndAlso, operands.Dequeue(), operands.Dequeue());\n\n        if (Operators.SameOperator(call.OperatorName, Operators.RuntimeOpOr))\n            return CompileLogical(LX.OrElse, operands.Dequeue(), operands.Dequeue());\n\n        var boundParameters = new List<LX>(methodParameters.Count);\n        foreach (var (pi, optional) in methodParameters)\n        {\n            if (pi.ParameterType == typeof(LogEventPropertyValue))\n            {\n                boundParameters.Add(operands.Count > 0\n                    ? operands.Dequeue()\n                    : LX.Constant(null, typeof(LogEventPropertyValue)));\n            }\n            else if (pi.ParameterType == typeof(StringComparison))\n                boundParameters.Add(LX.Constant(call.IgnoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal));\n            else if (pi.ParameterType == typeof(IFormatProvider))\n                boundParameters.Add(LX.Constant(_formatProvider, typeof(IFormatProvider)));\n            else if (pi.ParameterType == typeof(LogEvent))\n                boundParameters.Add(LX.Property(Context, EvaluationContextLogEventProperty));\n            else if (_nameResolver.TryBindFunctionParameter(pi, out var binding))\n                boundParameters.Add(LX.Constant(binding, pi.ParameterType));\n            else if (optional)\n                boundParameters.Add(LX.Constant(\n                    pi.GetCustomAttribute<DefaultParameterValueAttribute>()?.Value, pi.ParameterType));\n            else\n                throw new ArgumentException($\"The method `{m.Name}` implementing function `{call.OperatorName}` has argument `{pi.Name}` which could not be bound.\");\n        }\n\n        return LX.Call(m, boundParameters);\n    }\n\n    static string DescribeRequirements(IReadOnlyList<(string name, bool optional)> parameters)\n    {\n        static string DescribeArgument((string name, bool optional) p) =>\n            $\"`{p.name}`\" + (p.optional ? \" (optional)\" : \"\");\n\n        if (parameters.Count == 0)\n            return \"accepts no arguments\";\n\n        if (parameters.Count == 1)\n            return $\"accepts one argument, {DescribeArgument(parameters[0])}\";\n\n        if (parameters.Count == 2)\n            return $\"accepts two arguments, {DescribeArgument(parameters[0])} and {DescribeArgument(parameters[1])}\";\n\n        var result = new StringBuilder(\"accepts arguments\");\n        for (var i = 0; i < parameters.Count - 1; ++i)\n            result.Append($\" {DescribeArgument(parameters[i])},\");\n\n        result.Append($\" and {DescribeArgument(parameters[parameters.Count - 1])}\");\n        return result.ToString();\n    }\n\n    static ExpressionBody CompileLogical(Func<ExpressionBody, ExpressionBody, ExpressionBody> apply, ExpressionBody lhs, ExpressionBody rhs)\n    {\n        return LX.Convert(\n            LX.New(\n                typeof(ScalarValue).GetConstructor([typeof(object)])!,\n                LX.Convert(apply(\n                    LX.Call(CoerceToScalarBooleanMethod, lhs),\n                    LX.Call(CoerceToScalarBooleanMethod, rhs)), typeof(object))),\n            typeof(LogEventPropertyValue));\n    }\n\n    protected override ExpressionBody Transform(AccessorExpression spx)\n    {\n        var receiver = Transform(spx.Receiver);\n        return LX.Call(TryGetStructurePropertyValueMethod, LX.Constant(StringComparison.Ordinal), receiver, LX.Constant(spx.MemberName, typeof(string)));\n    }\n    protected override ExpressionBody Transform(ConstantExpression cx)\n    {\n        return LX.Constant(cx.Constant);\n    }\n\n    protected override ExpressionBody Transform(AmbientNameExpression px)\n    {\n        if (px.IsBuiltIn)\n        {\n            var formatter = new CompiledMessageToken(_formatProvider, null, TemplateTheme.None);\n            var formatProvider = _formatProvider;\n\n            return px.PropertyName switch\n            {\n                BuiltInProperty.Level => Splice(context => new ScalarValue(context.LogEvent.Level)),\n                BuiltInProperty.Message => Splice(context => new ScalarValue(Intrinsics.RenderMessage(formatter, context))),\n                BuiltInProperty.Exception => Splice(context =>\n                    context.LogEvent.Exception == null ? null : new ScalarValue(context.LogEvent.Exception)),\n                BuiltInProperty.TraceId => Splice(context =>\n                    context.LogEvent.TraceId == null ? null : new ScalarValue(context.LogEvent.TraceId.Value)),\n                BuiltInProperty.SpanId => Splice(context =>\n                    context.LogEvent.SpanId == null ? null : new ScalarValue(context.LogEvent.SpanId.Value)),\n                BuiltInProperty.Timestamp => Splice(context => new ScalarValue(context.LogEvent.Timestamp)),\n                BuiltInProperty.MessageTemplate => Splice(context => new ScalarValue(context.LogEvent.MessageTemplate.Text)),\n                BuiltInProperty.Properties => Splice(context =>\n                    new StructureValue(context.LogEvent.Properties.Select(kvp => new LogEventProperty(kvp.Key, kvp.Value)),\n                        null)),\n                BuiltInProperty.Renderings => Splice(context => Intrinsics.GetRenderings(context.LogEvent, formatProvider)),\n                BuiltInProperty.EventId => Splice(context =>\n                    new ScalarValue(EventIdHash.Compute(context.LogEvent.MessageTemplate.Text))),\n                var alias when _nameResolver.TryResolveBuiltInPropertyName(alias, out var target) =>\n                    Transform(new AmbientNameExpression(target, true)),\n                _ => LX.Constant(null, typeof(LogEventPropertyValue))\n            };\n        }\n\n        // Don't close over the AST node.\n        var propertyName = px.PropertyName;\n        return Splice(context => Intrinsics.GetPropertyValue(context, propertyName));\n    }\n\n    protected override ExpressionBody Transform(LocalNameExpression nlx)\n    {\n        // Don't close over the AST node.\n        var name = nlx.Name;\n        return Splice(context => Intrinsics.GetLocalValue(context, name));\n    }\n\n    protected override ExpressionBody Transform(Ast.LambdaExpression lmx)\n    {\n        var parameters = lmx.Parameters.Select(px => Tuple.Create(px, LX.Parameter(typeof(LogEventPropertyValue), px.ParameterName))).ToList();\n        var paramSwitcher = new ExpressionConstantMapper(parameters.ToDictionary(px => (object)px.Item1, px => (System.Linq.Expressions.Expression)px.Item2));\n        var rewritten = paramSwitcher.Visit(Transform(lmx.Body));\n\n        Type delegateType;\n        if (lmx.Parameters.Length == 1)\n            delegateType = typeof(Func<LogEventPropertyValue, LogEventPropertyValue>);\n        else if (lmx.Parameters.Length == 2)\n            delegateType = typeof(Func<LogEventPropertyValue, LogEventPropertyValue, LogEventPropertyValue>);\n        else\n            throw new NotSupportedException(\"Unsupported lambda signature.\");\n\n        var lambda = LX.Lambda(delegateType, rewritten!, parameters.Select(px => px.Item2).ToArray());\n\n        // Unfortunately, right now, functions need to be threaded through in constant scalar values :-D\n        return LX.New(typeof(ScalarValue).GetConstructor([typeof(object)])!,\n            LX.Convert(lambda, typeof(object)));\n    }\n\n    protected override ExpressionBody Transform(Ast.ParameterExpression prx)\n    {\n        // Will be within a lambda, which will subsequently sub-in the actual value.\n        // The `prx` placeholder needs to be wrapped in a `ScalarValue` so that eager\n        // typechecking doesn't fail before we've substituted the real value in.\n        return LX.Constant(new ScalarValue(prx), typeof(LogEventPropertyValue));\n    }\n\n    protected override ExpressionBody Transform(IndexerWildcardExpression wx)\n    {\n        return LX.Constant(null, typeof(LogEventPropertyValue));\n    }\n\n    protected override ExpressionBody Transform(ArrayExpression ax)\n    {\n        var elements = new List<ExpressionBody>(ax.Elements.Length);\n        var i = 0;\n        for (; i < ax.Elements.Length; ++i)\n        {\n            var element = ax.Elements[i];\n            if (element is ItemElement item)\n                elements.Add(Transform(item.Value));\n            else\n                break;\n        }\n\n        var arr = LX.NewArrayInit(typeof(LogEventPropertyValue), elements.ToArray());\n        var collected = LX.Call(CollectSequenceElementsMethod, arr);\n\n        for (; i < ax.Elements.Length; ++i)\n        {\n            var element = ax.Elements[i];\n            if (element is ItemElement item)\n                collected = LX.Call(ExtendSequenceValueWithItemMethod, collected, Transform(item.Value));\n            else\n            {\n                var spread = (SpreadElement) element;\n                collected = LX.Call(ExtendSequenceValueWithSpreadMethod, collected, Transform(spread.Content));\n            }\n        }\n\n        return LX.Call(ConstructSequenceValueMethod, collected);\n    }\n\n    protected override ExpressionBody Transform(ObjectExpression ox)\n    {\n        var names = new List<string>();\n        var values = new List<ExpressionBody>();\n\n        var i = 0;\n        for (; i < ox.Members.Length; ++i)\n        {\n            var member = ox.Members[i];\n            if (member is PropertyMember property)\n            {\n                if (names.Contains(property.Name))\n                {\n                    var oldPos = names.IndexOf(property.Name);\n                    values[oldPos] = Transform(property.Value);\n                }\n                else\n                {\n                    names.Add(property.Name);\n                    values.Add(Transform(property.Value));\n                }\n            }\n            else\n            {\n                break;\n            }\n        }\n\n        var namesConstant = LX.Constant(names.ToArray(), typeof(string[]));\n        var valuesArr = LX.NewArrayInit(typeof(LogEventPropertyValue), values.ToArray());\n        var properties = LX.Call(CollectStructurePropertiesMethod, namesConstant, valuesArr);\n\n        if (i == ox.Members.Length)\n        {\n            // No spreads; more efficient than `Complete*` because erasure is not required.\n            return LX.Call(ConstructStructureValueMethod, properties);\n        }\n\n        for (; i < ox.Members.Length; ++i)\n        {\n            var member = ox.Members[i];\n            if (member is PropertyMember property)\n            {\n                properties = LX.Call(\n                    ExtendStructureValueWithPropertyMethod,\n                    properties,\n                    LX.Constant(property.Name),\n                    Transform(property.Value));\n            }\n            else\n            {\n                var spread = (SpreadMember) member;\n                properties = LX.Call(\n                    ExtendStructureValueWithSpreadMethod,\n                    properties,\n                    Transform(spread.Content));\n            }\n        }\n\n        return LX.Call(CompleteStructureValueMethod, properties);\n    }\n\n    protected override ExpressionBody Transform(IndexerExpression ix)\n    {\n        return Transform(new CallExpression(false, Operators.OpElementAt, ix.Receiver, ix.Index));\n    }\n\n    protected override ExpressionBody Transform(IndexOfMatchExpression mx)\n    {\n        var rx = LX.Constant(mx.Regex);\n        var target = Transform(mx.Corpus);\n        return LX.Call(IndexOfMatchMethod, target, rx);\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Compilation/Linq/ParameterReplacementVisitor.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing System.Linq.Expressions;\n\nnamespace Serilog.Expressions.Compilation.Linq;\n\nclass ParameterReplacementVisitor : ExpressionVisitor\n{\n    readonly ParameterExpression[] _from, _to;\n\n    public static Expression ReplaceParameters(LambdaExpression lambda, params ParameterExpression[] newParameters)\n    {\n        var v = new ParameterReplacementVisitor(lambda.Parameters.ToArray(), newParameters);\n        return v.Visit(lambda.Body)!;\n    }\n\n    ParameterReplacementVisitor(ParameterExpression[] from, ParameterExpression[] to)\n    {\n        if (from == null) throw new ArgumentNullException(nameof(from));\n        if (to == null) throw new ArgumentNullException(nameof(to));\n        if (from.Length != to.Length) throw new InvalidOperationException(\"Mismatched parameter lists\");\n        _from = from;\n        _to = to;\n    }\n\n    protected override Expression VisitParameter(ParameterExpression node)\n    {\n        for (var i = 0; i < _from.Length; i++)\n        {\n            if (node == _from[i]) return _to[i];\n        }\n        return node;\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Compilation/OrderedNameResolver.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing System.Diagnostics.CodeAnalysis;\nusing System.Reflection;\n\nnamespace Serilog.Expressions.Compilation;\n\nclass OrderedNameResolver : NameResolver\n{\n    readonly NameResolver[] _orderedResolvers;\n\n    public OrderedNameResolver(IEnumerable<NameResolver> orderedResolvers)\n    {\n        _orderedResolvers = orderedResolvers.ToArray();\n    }\n\n    public override bool TryResolveFunctionName(string name, [MaybeNullWhen(false)] out MethodInfo implementation)\n    {\n        foreach (var resolver in _orderedResolvers)\n        {\n            if (resolver.TryResolveFunctionName(name, out implementation))\n                return true;\n        }\n\n        implementation = null;\n        return false;\n    }\n\n    public override bool TryBindFunctionParameter(ParameterInfo parameter, [MaybeNullWhen(false)] out object boundValue)\n    {\n        foreach (var resolver in _orderedResolvers)\n        {\n            if (resolver.TryBindFunctionParameter(parameter, out boundValue))\n                return true;\n        }\n\n        boundValue = null;\n        return false;\n    }\n\n    public override bool TryResolveBuiltInPropertyName(string alias, [NotNullWhen(true)] out string? target)\n    {\n        foreach (var resolver in _orderedResolvers)\n        {\n            if (resolver.TryResolveBuiltInPropertyName(alias, out target))\n                return true;\n        }\n\n        target = null;\n        return false;\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Compilation/Pattern.cs",
    "content": "// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing System.Diagnostics.CodeAnalysis;\nusing Serilog.Events;\nusing Serilog.Expressions.Ast;\n\nnamespace Serilog.Expressions.Compilation;\n\nstatic class Pattern\n{\n    public static bool IsAmbientProperty(Expression expression, string name, bool isBuiltIn)\n    {\n        return expression is AmbientNameExpression px &&\n               px.PropertyName == name &&\n               px.IsBuiltIn == isBuiltIn;\n    }\n\n    public static bool IsStringConstant(Expression expression, [MaybeNullWhen(false)] out string value)\n    {\n        if (expression is ConstantExpression cx &&\n            cx.Constant is ScalarValue sv &&\n            sv.Value is string s)\n        {\n            value = s;\n            return true;\n        }\n\n        value = null;\n        return false;\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Compilation/Properties/PropertiesObjectAccessorTransformer.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Expressions.Ast;\nusing Serilog.Expressions.Compilation.Transformations;\n\nnamespace Serilog.Expressions.Compilation.Properties;\n\nclass PropertiesObjectAccessorTransformer : IdentityTransformer\n{\n    public static Expression Rewrite(Expression actual)\n    {\n        return new PropertiesObjectAccessorTransformer().Transform(actual);\n    }\n\n    protected override Expression Transform(AccessorExpression ax)\n    {\n        if (!Pattern.IsAmbientProperty(ax.Receiver, BuiltInProperty.Properties, true))\n            return base.Transform(ax);\n\n        return new AmbientNameExpression(ax.MemberName, false);\n    }\n\n    protected override Expression Transform(IndexerExpression ix)\n    {\n        if (!Pattern.IsAmbientProperty(ix.Receiver, BuiltInProperty.Properties, true) ||\n            !Pattern.IsStringConstant(ix.Index, out var name))\n            return base.Transform(ix);\n\n        return new AmbientNameExpression(name, false);\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Compilation/Text/LikeSyntaxTransformer.cs",
    "content": "// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing System.Text.RegularExpressions;\nusing Serilog.Debugging;\nusing Serilog.Events;\nusing Serilog.Expressions.Ast;\nusing Serilog.Expressions.Compilation.Transformations;\n\nnamespace Serilog.Expressions.Compilation.Text;\n\nclass LikeSyntaxTransformer: IdentityTransformer\n{\n    static readonly LikeSyntaxTransformer Instance = new();\n\n    public static Expression Rewrite(Expression expression)\n    {\n        return Instance.Transform(expression);\n    }\n\n    protected override Expression Transform(CallExpression call)\n    {\n        if (call.Operands.Length != 2)\n            return base.Transform(call);\n\n        if (Operators.SameOperator(call.OperatorName, Operators.IntermediateOpLike))\n            return TryCompileLikeExpression(call.IgnoreCase, call.Operands[0], call.Operands[1]);\n\n        if (Operators.SameOperator(call.OperatorName, Operators.IntermediateOpNotLike))\n            return new CallExpression(\n                false,\n                Operators.RuntimeOpStrictNot,\n                TryCompileLikeExpression(call.IgnoreCase, call.Operands[0], call.Operands[1]));\n\n        return base.Transform(call);\n    }\n\n    Expression TryCompileLikeExpression(bool ignoreCase, Expression corpus, Expression like)\n    {\n        if (like is ConstantExpression { Constant: ScalarValue { Value: string s } })\n        {\n            var regex = LikeToRegex(s);\n            var opts = RegexOptions.Compiled | RegexOptions.ExplicitCapture;\n            if (ignoreCase)\n                opts |= RegexOptions.IgnoreCase;\n            var compiled = new Regex(regex, opts, TimeSpan.FromMilliseconds(100));\n            var indexof = new IndexOfMatchExpression(Transform(corpus), compiled);\n            return new CallExpression(ignoreCase, Operators.RuntimeOpNotEqual, indexof, new ConstantExpression(new ScalarValue(-1)));\n        }\n\n        SelfLog.WriteLine($\"Serilog.Expressions: `like` requires a constant string argument; found ${like}.\");\n        return new CallExpression(false, Operators.OpUndefined);\n    }\n\n    static string LikeToRegex(string like)\n    {\n        var begin = \"^\";\n        var regex = \"\";\n        var end = \"$\";\n\n        for (var i = 0; i < like.Length; ++i)\n        {\n            var ch = like[i];\n            var following = i == like.Length - 1 ? (char?)null : like[i + 1];\n            if (ch == '%')\n            {\n                if (following == '%')\n                {\n                    regex += '%';\n                    ++i;\n                }\n                else\n                {\n                    if (i == 0)\n                        begin = \"\";\n\n                    if (i == like.Length - 1)\n                        end = \"\";\n\n                    if (i == 0 && i == like.Length - 1)\n                        regex += \".*\";\n\n                    if (i != 0 && i != like.Length - 1)\n                        regex += \"(?:.|\\\\r|\\\\n)*\"; // ~= RegexOptions.Singleline\n                }\n            }\n            else if (ch == '_')\n            {\n                if (following == '_')\n                {\n                    regex += '_';\n                    ++i;\n                }\n                else\n                {\n                    regex += '.'; // Newlines aren't considered matches for _\n                }\n            }\n            else\n                regex += Regex.Escape(ch.ToString());\n        }\n\n        return begin + regex + end;\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Compilation/Text/TextMatchingTransformer.cs",
    "content": "// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing System.Text.RegularExpressions;\nusing Serilog.Debugging;\nusing Serilog.Events;\nusing Serilog.Expressions.Ast;\nusing Serilog.Expressions.Compilation.Transformations;\n\nnamespace Serilog.Expressions.Compilation.Text;\n\nclass TextMatchingTransformer: IdentityTransformer\n{\n    static readonly TextMatchingTransformer Instance = new();\n\n    public static Expression Rewrite(Expression expression)\n    {\n        return Instance.Transform(expression);\n    }\n\n    protected override Expression Transform(CallExpression call)\n    {\n        if (call.Operands.Length != 2)\n            return base.Transform(call);\n\n        if (Operators.SameOperator(call.OperatorName, Operators.OpIndexOfMatch))\n            return TryCompileIndexOfMatch(call.IgnoreCase, call.Operands[0], call.Operands[1]);\n\n        if (Operators.SameOperator(call.OperatorName, Operators.OpIsMatch))\n            return new CallExpression(\n                false,\n                Operators.RuntimeOpNotEqual,\n                TryCompileIndexOfMatch(call.IgnoreCase, call.Operands[0], call.Operands[1]),\n                new ConstantExpression(new ScalarValue(-1)));\n\n        return base.Transform(call);\n    }\n\n    Expression TryCompileIndexOfMatch(bool ignoreCase, Expression corpus, Expression regex)\n    {\n        if (regex is ConstantExpression { Constant: ScalarValue { Value: string s } })\n        {\n            try\n            {\n                var opts = RegexOptions.Compiled | RegexOptions.ExplicitCapture;\n                if (ignoreCase)\n                    opts |= RegexOptions.IgnoreCase;\n                var compiled = new Regex(s, opts, TimeSpan.FromMilliseconds(100));\n                return new IndexOfMatchExpression(Transform(corpus), compiled);\n            }\n            catch (ArgumentException ex)\n            {\n                throw new ExpressionValidationException($\"Invalid regular expression in IndexOfMatch: {ex.Message}\", ex);\n            }\n        }\n\n        SelfLog.WriteLine($\"Serilog.Expressions: `IndexOfMatch()` requires a constant string regular expression argument; found {regex}.\");\n        return new CallExpression(false, Operators.OpUndefined);\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Compilation/Transformations/IdentityTransformer.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Expressions.Ast;\n\nnamespace Serilog.Expressions.Compilation.Transformations;\n\nclass IdentityTransformer : SerilogExpressionTransformer<Expression>\n{\n    bool TryTransform(Expression expr, out Expression result)\n    {\n        result = Transform(expr);\n        return !ReferenceEquals(expr, result);\n    }\n\n    protected override Expression Transform(CallExpression call)\n    {\n        var any = false;\n        var operands = new List<Expression>();\n        foreach (var op in call.Operands)\n        {\n            if (TryTransform(op, out var result))\n                any = true;\n            operands.Add(result);\n        }\n\n        if (!any)\n            return call;\n\n        return new CallExpression(call.IgnoreCase, call.OperatorName, operands.ToArray());\n    }\n\n    protected override Expression Transform(ConstantExpression cx)\n    {\n        return cx;\n    }\n\n    protected override Expression Transform(AmbientNameExpression px)\n    {\n        return px;\n    }\n\n    protected override Expression Transform(LocalNameExpression nlx)\n    {\n        return nlx;\n    }\n\n    protected override Expression Transform(AccessorExpression spx)\n    {\n        if (!TryTransform(spx.Receiver, out var recv))\n            return spx;\n\n        return new AccessorExpression(recv, spx.MemberName);\n    }\n\n    protected override Expression Transform(LambdaExpression lmx)\n    {\n        if (!TryTransform(lmx.Body, out var body))\n            return lmx;\n\n        // By default we maintain the parameters available in the body\n        return new LambdaExpression(lmx.Parameters, body);\n    }\n\n    // Only touches uses of the parameters, not decls\n    protected override Expression Transform(ParameterExpression prx)\n    {\n        return prx;\n    }\n\n    protected override Expression Transform(IndexerWildcardExpression wx)\n    {\n        return wx;\n    }\n\n    protected override Expression Transform(ArrayExpression ax)\n    {\n        var any = false;\n        var elements = new List<Element>();\n        foreach (var el in ax.Elements)\n        {\n            if (el is ItemElement item)\n            {\n                if (TryTransform(item.Value, out var result))\n                    any = true;\n                elements.Add(new ItemElement(result));\n            }\n            else\n            {\n                var spread = (SpreadElement) el;\n                if (TryTransform(spread.Content, out var result))\n                    any = true;\n                elements.Add(new SpreadElement(result));\n            }\n        }\n\n        if (!any)\n            return ax;\n\n        return new ArrayExpression(elements.ToArray());\n    }\n\n    protected override Expression Transform(ObjectExpression ox)\n    {\n        var any = false;\n        var members = new List<Member>();\n        foreach (var m in ox.Members)\n        {\n            if (m is PropertyMember p)\n            {\n                if (TryTransform(p.Value, out var result))\n                    any = true;\n                members.Add(new PropertyMember(p.Name, result));\n            }\n            else\n            {\n                var s = (SpreadMember) m;\n                if (TryTransform(s.Content, out var result))\n                    any = true;\n                members.Add(new SpreadMember(result));\n            }\n        }\n\n        if (!any)\n            return ox;\n\n        return new ObjectExpression(members.ToArray());\n    }\n\n    protected override Expression Transform(IndexerExpression ix)\n    {\n        var transformedRecv = TryTransform(ix.Receiver, out var recv);\n\n        if (!TryTransform(ix.Index, out var index) && !transformedRecv)\n            return ix;\n\n        return new IndexerExpression(recv, index);\n    }\n\n    protected override Expression Transform(IndexOfMatchExpression mx)\n    {\n        if (!TryTransform(mx.Corpus, out var corpus))\n            return mx;\n\n        return new IndexOfMatchExpression(corpus, mx.Regex);\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Compilation/Transformations/NodeReplacer.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Expressions.Ast;\n\nnamespace Serilog.Expressions.Compilation.Transformations;\n\nclass NodeReplacer : IdentityTransformer\n{\n    readonly Expression _source;\n    readonly Expression _dest;\n\n    public static Expression Replace(Expression expr, Expression source, Expression dest)\n    {\n        var replacer = new NodeReplacer(source, dest);\n        return replacer.Transform(expr);\n    }\n\n    NodeReplacer(Expression source, Expression dest)\n    {\n        _source = source;\n        _dest = dest;\n    }\n\n    protected override Expression Transform(Expression x)\n    {\n        if (x == _source)\n            return _dest;\n\n        return base.Transform(x);\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Compilation/Transformations/SerilogExpressionTransformer.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Expressions.Ast;\n\nnamespace Serilog.Expressions.Compilation.Transformations;\n\nabstract class SerilogExpressionTransformer<TResult>\n{\n    protected virtual TResult Transform(Expression expression)\n    {\n        return expression switch\n        {\n            CallExpression call => Transform(call),\n            ConstantExpression constant => Transform(constant),\n            AccessorExpression accessor => Transform(accessor),\n            AmbientNameExpression property => Transform(property),\n            LocalNameExpression local => Transform(local),\n            LambdaExpression lambda => Transform(lambda),\n            ParameterExpression parameter => Transform(parameter),\n            IndexerWildcardExpression wildcard => Transform(wildcard),\n            ArrayExpression array => Transform(array),\n            ObjectExpression obj => Transform(obj),\n            IndexerExpression indexer => Transform(indexer),\n            IndexOfMatchExpression match => Transform(match),\n            null => throw new ArgumentNullException(nameof(expression)),\n            // Non-exhaustive because `InternalsVisibleTo` is applied to the assembly.\n            _ => throw new NotSupportedException($\"{expression} is not supported.\")\n        };\n    }\n\n    protected abstract TResult Transform(CallExpression call);\n    protected abstract TResult Transform(ConstantExpression cx);\n    protected abstract TResult Transform(AmbientNameExpression px);\n    protected abstract TResult Transform(LocalNameExpression nlx);\n    protected abstract TResult Transform(AccessorExpression spx);\n    protected abstract TResult Transform(LambdaExpression lmx);\n    protected abstract TResult Transform(ParameterExpression prx);\n    protected abstract TResult Transform(IndexerWildcardExpression wx);\n    protected abstract TResult Transform(ArrayExpression ax);\n    protected abstract TResult Transform(ObjectExpression ox);\n    protected abstract TResult Transform(IndexerExpression ix);\n    protected abstract TResult Transform(IndexOfMatchExpression mx);\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Compilation/Variadics/VariadicCallRewriter.cs",
    "content": "// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Expressions.Ast;\nusing Serilog.Expressions.Compilation.Transformations;\n\nnamespace Serilog.Expressions.Compilation.Variadics;\n\n// Handles variadic `coalesce()` and `concat()`, as well as optional arguments for other functions.\nclass VariadicCallRewriter : IdentityTransformer\n{\n    static readonly VariadicCallRewriter Instance = new();\n\n    public static Expression Rewrite(Expression expression)\n    {\n        return Instance.Transform(expression);\n    }\n\n    protected override Expression Transform(CallExpression call)\n    {\n        if (Operators.SameOperator(call.OperatorName, Operators.OpCoalesce) ||\n            Operators.SameOperator(call.OperatorName, Operators.OpConcat))\n        {\n            if (call.Operands.Length == 0)\n                return new CallExpression(false, Operators.OpUndefined);\n            if (call.Operands.Length == 1)\n                return Transform(call.Operands.Single());\n            if (call.Operands.Length > 2)\n            {\n                var first = Transform(call.Operands.First());\n                return new CallExpression(call.IgnoreCase, call.OperatorName, first,\n                    Transform(new CallExpression(call.IgnoreCase, call.OperatorName, call.Operands.Skip(1).ToArray())));\n            }\n        }\n\n        return base.Transform(call);\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Compilation/Wildcards/WildcardComprehensionTransformer.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Expressions.Ast;\nusing Serilog.Expressions.Compilation.Transformations;\n\nnamespace Serilog.Expressions.Compilation.Wildcards;\n\nclass WildcardComprehensionTransformer : IdentityTransformer\n{\n    int _nextParameter;\n\n    public static Expression Rewrite(Expression root)\n    {\n        var wc = new WildcardComprehensionTransformer();\n        return wc.Transform(root);\n    }\n\n    // This matches expression fragments such as `A[?] = 'test'` and\n    // transforms them into `any(A, |p| p = 'test)`.\n    //\n    // As the comparand in such expressions can be complex, e.g.\n    // `Substring(A[?], 0, 4) = 'test')`, the search for `?` and `*` wildcards\n    // is deep, but, it terminates upon reaching any other wildcard-compatible\n    // comparison. Thus `(A[?] = 'test') = true` will result in `any(A, |p| p = 'test') = true` and\n    // not `any(A, |p| (p = 'test') = true)`, which is important because short-circuiting when the first\n    // argument to `any()` is undefined will change the semantics of the resulting expression, otherwise.\n    protected override Expression Transform(CallExpression lx)\n    {\n        if (!Operators.WildcardComparators.Contains(lx.OperatorName))\n            return base.Transform(lx);\n\n        IndexerExpression? indexer = null;\n        Expression? wildcardPath = null;\n        var indexerOperand = -1;\n        for (var i = 0; i < lx.Operands.Length; ++i)\n        {\n            indexer = WildcardSearch.FindWildcardIndexer(lx.Operands[i]);\n            if (indexer != null)\n            {\n                indexerOperand = i;\n                wildcardPath = lx.Operands[i];\n                break;\n            }\n        }\n\n        if (indexer == null || wildcardPath == null)\n            return base.Transform(lx); // N/A, or invalid\n\n        var px = new ParameterExpression(\"p\" + _nextParameter++);\n        var nestedComparand = NodeReplacer.Replace(wildcardPath, indexer, px);\n\n        var coll = indexer.Receiver;\n        var wc = ((IndexerWildcardExpression)indexer.Index).Wildcard;\n\n        var comparisonArgs = lx.Operands.ToArray();\n        comparisonArgs[indexerOperand] = nestedComparand;\n        var body = new CallExpression(lx.IgnoreCase, lx.OperatorName, comparisonArgs);\n\n        var lambda = new LambdaExpression([px], body);\n\n        var op = Operators.ToRuntimeWildcardOperator(wc);\n        var call = new CallExpression(false, op, coll, lambda);\n        return Transform(call);\n    }\n\n    // Detects and transforms standalone `A[?]` fragments that are not part of a comparision; these\n    // are effectively Boolean tests.\n    protected override Expression Transform(IndexerExpression ix)\n    {\n        if (ix.Index is not IndexerWildcardExpression wx)\n            return base.Transform(ix);\n\n        var px = new ParameterExpression(\"p\" + _nextParameter++);\n        var coll = Transform(ix.Receiver);\n        var lambda = new LambdaExpression([px], px);\n        var op = Operators.ToRuntimeWildcardOperator(wx.Wildcard);\n        return new CallExpression(false, op, coll, lambda);\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Compilation/Wildcards/WildcardSearch.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Expressions.Ast;\nusing Serilog.Expressions.Compilation.Transformations;\n\nnamespace Serilog.Expressions.Compilation.Wildcards;\n\nclass WildcardSearch : SerilogExpressionTransformer<IndexerExpression?>\n{\n    static readonly WildcardSearch Instance = new();\n\n    public static IndexerExpression? FindWildcardIndexer(Expression fx)\n    {\n        return Instance.Transform(fx);\n    }\n\n    protected override IndexerExpression? Transform(IndexerExpression ix)\n    {\n        if (ix.Index is IndexerWildcardExpression)\n            return ix;\n\n        return Transform(ix.Receiver);\n    }\n\n    protected override IndexerExpression? Transform(ConstantExpression cx)\n    {\n        return null;\n    }\n\n    protected override IndexerExpression? Transform(AmbientNameExpression px)\n    {\n        return null;\n    }\n\n    protected override IndexerExpression? Transform(LocalNameExpression nlx)\n    {\n        return null;\n    }\n\n    protected override IndexerExpression? Transform(AccessorExpression spx)\n    {\n        return Transform(spx.Receiver);\n    }\n\n    protected override IndexerExpression? Transform(LambdaExpression lmx)\n    {\n        return null;\n    }\n\n    protected override IndexerExpression? Transform(ParameterExpression prx)\n    {\n        return null;\n    }\n\n    protected override IndexerExpression? Transform(IndexerWildcardExpression wx)\n    {\n        // Must be within an indexer\n        return null;\n    }\n\n    protected override IndexerExpression? Transform(ArrayExpression ax)\n    {\n        return null;\n    }\n\n    protected override IndexerExpression? Transform(CallExpression call)\n    {\n        // If we hit a wildcard-compatible operation, then any wildcards within its operands \"belong\" to\n        // it and can't be the result of this search.\n        if (Operators.WildcardComparators.Contains(call.OperatorName))\n            return null;\n\n        return call.Operands.Select(Transform).FirstOrDefault(e => e != null);\n    }\n\n    protected override IndexerExpression? Transform(IndexOfMatchExpression mx)\n    {\n        return Transform(mx.Corpus);\n    }\n\n    protected override IndexerExpression? Transform(ObjectExpression ox)\n    {\n        return null;\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/CompiledExpression.cs",
    "content": "﻿// Copyright Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Events;\n\nnamespace Serilog.Expressions;\n\n/// <summary>\n/// A compiled expression evaluated against a <see cref=\"LogEvent\"/>.\n/// </summary>\n/// <param name=\"logEvent\"></param>\n/// <returns>The result of evaluating the expression, represented as a <see cref=\"LogEventPropertyValue\"/>,\n/// or <c langword=\"null\">null</c> if the result is undefined.</returns>\npublic delegate LogEventPropertyValue? CompiledExpression(LogEvent logEvent);"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Evaluatable.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Events;\n\nnamespace Serilog.Expressions;\n\ndelegate LogEventPropertyValue? Evaluatable(EvaluationContext ctx);"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/EvaluationContext.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Events;\nusing Serilog.Expressions.Runtime;\n\nnamespace Serilog.Expressions;\n\nreadonly struct EvaluationContext\n{\n    public LogEvent LogEvent { get; }\n    public Locals? Locals { get; }\n\n    public EvaluationContext(LogEvent logEvent, Locals? locals = null)\n    {\n        LogEvent = logEvent ?? throw new ArgumentNullException(nameof(logEvent));\n        Locals = locals;\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/ExpressionResult.cs",
    "content": "﻿// Copyright Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Events;\nusing Serilog.Expressions.Runtime;\n\nnamespace Serilog.Expressions;\n\n/// <summary>\n/// Helper functions for working with the results of evaluating expressions.\n/// </summary>\npublic static class ExpressionResult\n{\n    /// <summary>\n    /// Test whether <paramref name=\"value\"/> is <c langword=\"true\">true</c>.\n    /// </summary>\n    /// <param name=\"value\">The value to test, which may be <c langword=\"null\">null</c>\n    /// if the result of evaluating an expression was undefined.</param>\n    /// <returns>Returns <c langword=\"true\">true</c> if and only if the\n    /// <paramref name=\"value\"/> is a scalar Boolean with the\n    /// value <c langword=\"true\">true</c>. Returns <c langword=\"false\">false</c>, otherwise.</returns>\n    public static bool IsTrue(LogEventPropertyValue? value)\n    {\n        return Coerce.IsTrue(value);\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Helpers.cs",
    "content": "﻿// Copyright Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing System.Text.RegularExpressions;\n\n#if NO_CI_STRING_CONTAINS\n\nnamespace Serilog.Expressions\n{\n    /// <summary>\n    /// Helper methods.\n    /// </summary>\n    static class Helpers\n    {\n        /// <summary>\n        /// Backport .NET Standard 2.1 additions to maintain .NET Standard 2.0 compatibility.\n        /// Returns a value indicating whether a specified string occurs within this string, using the specified comparison rules.\n        ///\n        /// From;\n        /// https://github.com/dotnet/runtime/issues/22198\n        /// https://stackoverflow.com/questions/444798/case-insensitive-containsstring/444818#444818\n        /// </summary>\n        /// <param name=\"this\">input string</param>\n        /// <param name=\"value\">The string to seek.</param>\n        /// <param name=\"comparisonType\">Specifies the rule to use in the comparison.</param>\n        /// <returns></returns>\n        public static bool Contains(this string @this, string value, StringComparison comparisonType)\n        {\n            return @this.IndexOf(value, comparisonType) >= 0;\n        }\n\n        public static string Replace(this string @this, string oldValue, string newValue, StringComparison comparisonType)\n        {\n            if (\"a\".Equals(\"A\", comparisonType))\n            {\n                return Regex.Replace(\n                    @this,\n                    Regex.Escape(oldValue),\n                    newValue.Replace(\"$\", \"$$\"),\n                    RegexOptions.IgnoreCase);\n            }\n\n            return @this.Replace(oldValue, newValue);\n        }\n    }\n}\n#endif"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/LoggingFilterSwitch.cs",
    "content": "﻿// Copyright Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Core;\nusing Serilog.Events;\nusing Serilog.Expressions.Runtime;\n\nnamespace Serilog.Expressions;\n\n/// <summary>\n/// A log event filter that can be modified at runtime.\n/// </summary>\npublic class LoggingFilterSwitch : ILogEventFilter\n{\n    // Reference assignments are atomic. While this class makes\n    // no attempt to synchronize Expression, ToString(), and IsIncluded(),\n    // for any observer, this at least ensures they won't be permanently out-of-sync for\n    // all observers.\n    volatile Tuple<string, CompiledExpression>? _filter;\n\n    /// <summary>\n    /// Construct a <see cref=\"LoggingFilterSwitch\"/>, optionally initialized\n    /// with the <paramref name=\"expression\"/>.\n    /// </summary>\n    /// <param name=\"expression\">A filter expression against which log events will be tested.\n    /// Only expressions that evaluate to <c>true</c> are included\n    /// by the filter. A <c>null</c> expression will accept all\n    /// events.</param>\n    public LoggingFilterSwitch(string? expression = null)\n    {\n        Expression = expression;\n    }\n\n    /// <summary>\n    /// A filter expression against which log events will be tested.\n    /// Only expressions that evaluate to <c>true</c> are included\n    /// by the filter. A <c>null</c> expression will accept all\n    /// events.\n    /// </summary>\n    public string? Expression\n    {\n        // ReSharper disable once UnusedMember.Global\n        get\n        {\n            var filter = _filter;\n            return filter?.Item1;\n        }\n        set\n        {\n            if (value == null)\n            {\n                _filter = null;\n            }\n            else\n            {\n                _filter = new(\n                    value,\n                    SerilogExpression.Compile(value));\n            }\n        }\n    }\n\n    /// <inheritdoc/>\n    public bool IsEnabled(LogEvent logEvent)\n    {\n        if (logEvent == null) throw new ArgumentNullException(nameof(logEvent));\n\n        var filter = _filter;\n\n        if (filter == null)\n            return true;\n\n        return Coerce.IsTrue(filter.Item2(logEvent));\n    }\n\n    /// <inheritdoc/>\n    public override string ToString()\n    {\n        var filter = _filter;\n        return filter?.Item1 ?? \"\";\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/NameResolver.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing System.Diagnostics.CodeAnalysis;\nusing System.Reflection;\nusing Serilog.Events;\n\nnamespace Serilog.Expressions;\n\n/// <summary>\n/// Looks up the implementations of functions that appear in expressions.\n/// </summary>\npublic abstract class NameResolver\n{\n    /// <summary>\n    /// Match a function name to a method that implements it.\n    /// </summary>\n    /// <param name=\"name\">The function name as it appears in the expression source. Names are not case-sensitive.</param>\n    /// <param name=\"implementation\">A <see cref=\"MethodInfo\"/> implementing the function.</param>\n    /// <returns><c>True</c> if the name could be resolved; otherwise, <c>false</c>.</returns>\n    /// <remarks>The method implementing a function should be <c>static</c>, return <see cref=\"LogEventPropertyValue\"/>,\n    /// and accept parameters of type <see cref=\"LogEventPropertyValue\"/>. If the <c>ci</c> modifier is supported,\n    /// a <see cref=\"StringComparison\"/> should be included in the argument list. If the function is culture-specific,\n    /// an <see cref=\"IFormatProvider\"/> should be included in the argument list.</remarks>\n    public virtual bool TryResolveFunctionName(string name, [MaybeNullWhen(false)] out MethodInfo implementation)\n    {\n        implementation = null;\n        return false;\n    }\n\n    /// <summary>\n    /// Provide a value for a non-<see cref=\"LogEventPropertyValue\"/> parameter. This allows user-defined state to\n    /// be threaded through user-defined functions.\n    /// </summary>\n    /// <param name=\"parameter\">A parameter of a method implementing a user-defined function, which could not be\n    /// bound to any of the standard runtime-provided values or operands.</param>\n    /// <param name=\"boundValue\">The value that should be provided when the method is called.</param>\n    /// <returns><c>True</c> if the parameter could be bound; otherwise, <c>false</c>.</returns>\n    public virtual bool TryBindFunctionParameter(ParameterInfo parameter, [MaybeNullWhen(false)] out object boundValue)\n    {\n        boundValue = null;\n        return false;\n    }\n\n    /// <summary>\n    /// Map an unrecognized built-in property name to a recognised one.\n    /// </summary>\n    /// <remarks>Intended predominantly to support migration from <em>Serilog.Filters.Expressions</em>.</remarks>\n    /// <param name=\"alias\">The unrecognized name, for example, <code>\"Message\"</code>; the <code>@</code> prefix is\n    /// not included.</param>\n    /// <param name=\"target\">If the name could be resolved, the target property name, without any prefix; for\n    /// example, <code>\"m\"</code>.</param>\n    /// <returns>True if the alias was mapped to a built-in property; otherwise, false.</returns>\n    public virtual bool TryResolveBuiltInPropertyName(string alias, [NotNullWhen(true)] out string? target)\n    {\n        target = null;\n        return false;\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Operators.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Expressions.Ast;\n\n// ReSharper disable InconsistentNaming, MemberCanBePrivate.Global\n\nnamespace Serilog.Expressions;\n\nstatic class Operators\n{\n    static StringComparer OperatorComparer { get; } = StringComparer.OrdinalIgnoreCase;\n\n    // Core filter language\n    // Op* means usable in expressions _and_ runtime executable.\n    // RuntimeOp* means runtime only.\n\n    public const string OpCoalesce = \"Coalesce\";\n    public const string OpConcat = \"Concat\";\n    public const string OpContains = \"Contains\";\n    public const string OpElementAt = \"ElementAt\";\n    public const string OpEndsWith = \"EndsWith\";\n    public const string OpIndexOf = \"IndexOf\";\n    public const string OpIndexOfMatch = \"IndexOfMatch\";\n    public const string OpInspect = \"Inspect\";\n    public const string OpIsMatch = \"IsMatch\";\n    public const string OpIsDefined = \"IsDefined\";\n    public const string OpLastIndexOf = \"LastIndexOf\";\n    public const string OpLength = \"Length\";\n    public const string OpNest = \"Nest\";\n    public const string OpNow = \"Now\";\n    public const string OpReplace = \"Replace\";\n    public const string OpRound = \"Round\";\n    public const string OpStartsWith = \"StartsWith\";\n    public const string OpSubstring = \"Substring\";\n    public const string OpTagOf = \"TagOf\";\n    public const string OpToString = \"ToString\";\n    public const string OpTypeOf = \"TypeOf\";\n    public const string OpUndefined = \"Undefined\";\n    public const string OpUtcDateTime = \"UtcDateTime\";\n\n    public const string IntermediateOpLike = \"_Internal_Like\";\n    public const string IntermediateOpNotLike = \"_Internal_NotLike\";\n\n    public const string RuntimeOpAdd = \"_Internal_Add\";\n    public const string RuntimeOpSubtract = \"_Internal_Subtract\";\n    public const string RuntimeOpMultiply = \"_Internal_Multiply\";\n    public const string RuntimeOpDivide = \"_Internal_Divide\";\n    public const string RuntimeOpModulo = \"_Internal_Modulo\";\n    public const string RuntimeOpPower = \"_Internal_Power\";\n    public const string RuntimeOpAnd = \"_Internal_And\";\n    public const string RuntimeOpOr = \"_Internal_Or\";\n    public const string RuntimeOpLessThanOrEqual = \"_Internal_LessThanOrEqual\";\n    public const string RuntimeOpLessThan = \"_Internal_LessThan\";\n    public const string RuntimeOpGreaterThan = \"_Internal_GreaterThan\";\n    public const string RuntimeOpGreaterThanOrEqual = \"_Internal_GreaterThanOrEqual\";\n    public const string RuntimeOpEqual = \"_Internal_Equal\";\n    public const string RuntimeOpNotEqual = \"_Internal_NotEqual\";\n    public const string RuntimeOpNegate = \"_Internal_Negate\";\n    public const string RuntimeOpNot = \"_Internal_Not\";\n    public const string RuntimeOpAny = \"_Internal_Any\";\n    public const string RuntimeOpAll = \"_Internal_All\";\n    public const string RuntimeOpIsNull = \"_Internal_IsNull\";\n    public const string RuntimeOpIsNotNull = \"_Internal_IsNotNull\";\n    public const string RuntimeOpIn = \"_Internal_In\";\n    public const string RuntimeOpNotIn = \"_Internal_NotIn\";\n    public const string RuntimeOpStrictNot = \"_Internal_StrictNot\";\n    public const string RuntimeOpIfThenElse = \"_Internal_IfThenElse\";\n\n    public static readonly HashSet<string> WildcardComparators = new(OperatorComparer)\n    {\n        OpContains,\n        OpStartsWith,\n        OpEndsWith,\n        RuntimeOpNotEqual,\n        RuntimeOpEqual,\n        RuntimeOpLessThan,\n        RuntimeOpLessThanOrEqual,\n        RuntimeOpGreaterThan,\n        RuntimeOpGreaterThanOrEqual,\n        IntermediateOpLike,\n        IntermediateOpNotLike,\n        RuntimeOpIn,\n        RuntimeOpNotIn,\n        OpIsMatch,\n        OpIsDefined,\n        RuntimeOpIsNull,\n        RuntimeOpIsNotNull,\n        RuntimeOpAny,\n        RuntimeOpAll\n    };\n\n    public static bool SameOperator(string op1, string op2)\n    {\n        if (op1 == null) throw new ArgumentNullException(nameof(op1));\n        if (op2 == null) throw new ArgumentNullException(nameof(op2));\n\n        return OperatorComparer.Equals(op1, op2);\n    }\n\n    public static string ToRuntimeWildcardOperator(IndexerWildcard wildcard)\n    {\n        return wildcard switch\n        {\n            IndexerWildcard.All => RuntimeOpAll,\n            IndexerWildcard.Any => RuntimeOpAny,\n            _ => throw new ArgumentException(\"Unsupported wildcard.\")\n        };\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Parsing/Combinators.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.ParserConstruction;\nusing Serilog.ParserConstruction.Model;\n\nnamespace Serilog.Expressions.Parsing;\n\nstatic class Combinators\n{\n    public static TokenListParser<TKind, TResult> ChainModified<TKind, TResult, TOperator, TModifier>(\n        TokenListParser<TKind, TOperator> @operator,\n        TokenListParser<TKind, TResult> operand,\n        TokenListParser<TKind, TModifier> modify,\n        Func<TOperator, TResult, TResult, TModifier, TResult> apply)\n    {\n        if (@operator == null)\n            throw new ArgumentNullException(nameof (@operator));\n        if (operand == null)\n            throw new ArgumentNullException(nameof (operand));\n        if (modify == null) throw new ArgumentNullException(nameof(modify));\n        if (apply == null)\n            throw new ArgumentNullException(nameof (apply));\n\n        return input =>\n        {\n            var parseResult = operand(input);\n            if (!parseResult.HasValue )\n                return parseResult;\n\n            var result = parseResult.Value;\n            var remainder = parseResult.Remainder;\n\n            var operatorResult = @operator(remainder);\n            while (operatorResult.HasValue || operatorResult.SubTokenErrorPosition.HasValue || remainder != operatorResult.Remainder)\n            {\n                // If operator read any input, but failed to read complete input, we return error\n                if (!operatorResult.HasValue)\n                    return TokenListParserResult.CastEmpty<TKind, TOperator, TResult>(operatorResult);\n\n                var operandResult = operand(operatorResult.Remainder);\n                remainder = operandResult.Remainder;\n\n                if (!operandResult.HasValue)\n                    return operandResult;\n\n                var modifierResult = modify(remainder);\n                remainder = modifierResult.Remainder;\n\n                if (!modifierResult.HasValue)\n                    return TokenListParserResult.CastEmpty<TKind, TModifier, TResult>(modifierResult);\n\n                result = apply(operatorResult.Value, result, operandResult.Value, modifierResult.Value);\n\n                operatorResult = @operator(remainder);\n            }\n\n            return TokenListParserResult.Value(result, input, remainder);\n        };\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Parsing/ExpressionKeyword.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nnamespace Serilog.Expressions.Parsing;\n\nreadonly struct ExpressionKeyword\n{\n    public string Text { get; }\n    public ExpressionToken Token { get; }\n\n    public ExpressionKeyword(string text, ExpressionToken token)\n    {\n        Text = text ?? throw new ArgumentNullException(nameof(text));\n        Token = token;\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Parsing/ExpressionParser.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing System.Diagnostics.CodeAnalysis;\nusing Serilog.Expressions.Ast;\n\nnamespace Serilog.Expressions.Parsing;\n\nclass ExpressionParser\n{\n    readonly ExpressionTokenizer _tokenizer = new();\n\n    public Expression Parse(string expression)\n    {\n        if (!TryParse(expression, out var root, out var error))\n            throw new ArgumentException(error);\n\n        return root;\n    }\n\n    public bool TryParse(string filterExpression,\n        [MaybeNullWhen(false)] out Expression root, [MaybeNullWhen(true)] out string error)\n    {\n        if (filterExpression == null) throw new ArgumentNullException(nameof(filterExpression));\n\n        var tokenList = _tokenizer.TryTokenize(filterExpression);\n        if (!tokenList.HasValue)\n        {\n            error = tokenList.ToString();\n            root = null;\n            return false;\n        }\n\n        var result = ExpressionTokenParsers.TryParse(tokenList.Value);\n        if (!result.HasValue)\n        {\n            error = result.ToString();\n            root = null;\n            return false;\n        }\n\n        root = result.Value;\n        error = null;\n        return true;\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Parsing/ExpressionTextParsers.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.ParserConstruction;\nusing Serilog.ParserConstruction.Model;\nusing Serilog.ParserConstruction.Parsers;\n\nnamespace Serilog.Expressions.Parsing;\n\nstatic class ExpressionTextParsers\n{\n    static readonly TextParser<ExpressionToken> LessOrEqual = Span.EqualTo(\"<=\").Value(ExpressionToken.LessThanOrEqual);\n    static readonly TextParser<ExpressionToken> GreaterOrEqual = Span.EqualTo(\">=\").Value(ExpressionToken.GreaterThanOrEqual);\n    static readonly TextParser<ExpressionToken> NotEqual = Span.EqualTo(\"<>\").Value(ExpressionToken.NotEqual);\n    static readonly TextParser<ExpressionToken> Spread = Span.EqualTo(\"..\").Value(ExpressionToken.Spread);\n\n    public static readonly TextParser<ExpressionToken> CompoundOperator = GreaterOrEqual.Or(LessOrEqual.Try().Or(NotEqual)).Or(Spread);\n\n    public static readonly TextParser<string> HexInteger =\n        Span.EqualTo(\"0x\")\n            .IgnoreThen(Character.Digit.Or(Character.Matching(ch => ch is >= 'a' and <= 'f' or >= 'A' and <= 'F', \"a-f\"))\n                .Named(\"hex digit\")\n                .AtLeastOnce())\n            .Select(chars => new string(chars));\n\n    static readonly TextParser<char> StringContentChar =\n        Span.EqualTo(\"''\").Value('\\'').Try().Or(Character.Except('\\''));\n\n    public static readonly TextParser<string> String =\n        Character.EqualTo('\\'')\n            .IgnoreThen(StringContentChar.Many())\n            .Then(s => Character.EqualTo('\\'').Value(new string(s)));\n\n    public static readonly TextParser<TextSpan> Real =\n        Numerics.Integer\n            .Then(n => Character.EqualTo('.').IgnoreThen(Numerics.Integer).OptionalOrDefault()\n                .Select(f => f == TextSpan.None ? n : new(n.Source!, n.Position, n.Length + f.Length + 1)));\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Parsing/ExpressionToken.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.ParserConstruction.Display;\n\nnamespace Serilog.Expressions.Parsing;\n\nenum ExpressionToken\n{\n    None,\n\n    Identifier,\n\n    [Token(Description = \"built-in identifier\")]\n    BuiltInIdentifier,\n\n    String,\n\n    Number,\n\n    [Token(Description = \"hexadecimal number\")]\n    HexNumber,\n\n    [Token(Example = \",\")]\n    Comma,\n\n    [Token(Example = \".\")]\n    Period,\n\n    [Token(Example = \"..\")]\n    Spread,\n\n    [Token(Example = \"[\")]\n    LBracket,\n\n    [Token(Example = \"]\")]\n    RBracket,\n\n    [Token(Example = \"(\")]\n    LParen,\n\n    [Token(Example = \")\")]\n    RParen,\n\n    [Token(Example = \"{\")]\n    LBrace,\n\n    [Token(Example = \"}\")]\n    RBrace,\n\n    [Token(Example = \":\")]\n    Colon,\n\n    [Token(Example = \"?\")]\n    QuestionMark,\n\n    [Token(Category = \"operator\", Example = \"+\")]\n    Plus,\n\n    [Token(Category = \"operator\", Example = \"-\")]\n    Minus,\n\n    [Token(Example = \"*\")]\n    Asterisk,\n\n    [Token(Category = \"operator\", Example = \"/\")]\n    ForwardSlash,\n\n    [Token(Category = \"operator\", Example = \"%\")]\n    Percent,\n\n    [Token(Category = \"operator\", Example = \"^\")]\n    Caret,\n\n    [Token(Category = \"operator\", Example = \"<\")]\n    LessThan,\n\n    [Token(Category = \"operator\", Example = \"<=\")]\n    LessThanOrEqual,\n\n    [Token(Category = \"operator\", Example = \">\")]\n    GreaterThan,\n\n    [Token(Category = \"operator\", Example = \">=\")]\n    GreaterThanOrEqual,\n\n    [Token(Category = \"operator\", Example = \"=\")]\n    Equal,\n\n    [Token(Category = \"operator\", Example = \"<>\")]\n    NotEqual,\n\n    [Token(Category = \"keyword\", Example = \"and\")]\n    And,\n\n    [Token(Category = \"keyword\", Example = \"in\")]\n    In,\n\n    [Token(Category = \"keyword\", Example = \"is\")]\n    Is,\n\n    [Token(Category = \"keyword\", Example = \"like\")]\n    Like,\n\n    [Token(Category = \"keyword\", Example = \"not\")]\n    Not,\n\n    [Token(Category = \"keyword\", Example = \"or\")]\n    Or,\n\n    [Token(Category = \"keyword\", Example = \"true\")]\n    True,\n\n    [Token(Category = \"keyword\", Example = \"false\")]\n    False,\n\n    [Token(Category = \"keyword\", Example = \"null\")]\n    Null,\n\n    [Token(Category = \"keyword\", Example = \"if\")]\n    If,\n\n    [Token(Category = \"keyword\", Example = \"then\")]\n    Then,\n\n    [Token(Category = \"keyword\", Example = \"else\")]\n    Else,\n\n    [Token(Category = \"keyword\", Example = \"ci\")]\n    CI,\n\n    // Template syntax\n\n    [Token(Description = \"text\")]\n    Text,\n\n    [Token(Example = \"{{\")]\n    DoubleLBrace,\n\n    [Token(Example = \"}}\")]\n    DoubleRBrace,\n\n    [Token(Example = \"{#\")]\n    LBraceHash,\n\n    [Token(Description = \"format specifier\")]\n    Format,\n\n    [Token(Category = \"keyword\", Example = \"end\")]\n    End,\n\n    [Token(Category = \"keyword\", Example = \"each\")]\n    Each,\n\n    [Token(Category = \"keyword\", Example = \"delimit\")]\n    Delimit,\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Parsing/ExpressionTokenParsers.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing System.Globalization;\nusing Serilog.Events;\nusing Serilog.Expressions.Ast;\nusing Serilog.ParserConstruction;\nusing Serilog.ParserConstruction.Model;\nusing Serilog.ParserConstruction.Parsers;\n\nnamespace Serilog.Expressions.Parsing;\n\nstatic class ExpressionTokenParsers\n{\n    public static TokenListParserResult<ExpressionToken, Expression> TryParse(\n        TokenList<ExpressionToken> input)\n    {\n        return Expr.AtEnd().TryParse(input);\n    }\n\n    public static TokenListParserResult<ExpressionToken, Expression> TryPartialParse(\n        TokenList<ExpressionToken> input)\n    {\n        return Expr.TryParse(input);\n    }\n\n    static readonly TokenListParser<ExpressionToken, string> Add = Token.EqualTo(ExpressionToken.Plus).Value(Operators.RuntimeOpAdd);\n    static readonly TokenListParser<ExpressionToken, string> Subtract = Token.EqualTo(ExpressionToken.Minus).Value(Operators.RuntimeOpSubtract);\n    static readonly TokenListParser<ExpressionToken, string> Multiply = Token.EqualTo(ExpressionToken.Asterisk).Value(Operators.RuntimeOpMultiply);\n    static readonly TokenListParser<ExpressionToken, string> Divide = Token.EqualTo(ExpressionToken.ForwardSlash).Value(Operators.RuntimeOpDivide);\n    static readonly TokenListParser<ExpressionToken, string> Modulo = Token.EqualTo(ExpressionToken.Percent).Value(Operators.RuntimeOpModulo);\n    static readonly TokenListParser<ExpressionToken, string> Power = Token.EqualTo(ExpressionToken.Caret).Value(Operators.RuntimeOpPower);\n    static readonly TokenListParser<ExpressionToken, string> And = Token.EqualTo(ExpressionToken.And).Value(Operators.RuntimeOpAnd);\n    static readonly TokenListParser<ExpressionToken, string> Or = Token.EqualTo(ExpressionToken.Or).Value(Operators.RuntimeOpOr);\n    static readonly TokenListParser<ExpressionToken, string> Lte = Token.EqualTo(ExpressionToken.LessThanOrEqual).Value(Operators.RuntimeOpLessThanOrEqual);\n    static readonly TokenListParser<ExpressionToken, string> Lt = Token.EqualTo(ExpressionToken.LessThan).Value(Operators.RuntimeOpLessThan);\n    static readonly TokenListParser<ExpressionToken, string> Gt = Token.EqualTo(ExpressionToken.GreaterThan).Value(Operators.RuntimeOpGreaterThan);\n    static readonly TokenListParser<ExpressionToken, string> Gte = Token.EqualTo(ExpressionToken.GreaterThanOrEqual).Value(Operators.RuntimeOpGreaterThanOrEqual);\n    static readonly TokenListParser<ExpressionToken, string> Eq = Token.EqualTo(ExpressionToken.Equal).Value(Operators.RuntimeOpEqual);\n    static readonly TokenListParser<ExpressionToken, string> Neq = Token.EqualTo(ExpressionToken.NotEqual).Value(Operators.RuntimeOpNotEqual);\n    static readonly TokenListParser<ExpressionToken, string> Negate = Token.EqualTo(ExpressionToken.Minus).Value(Operators.RuntimeOpNegate);\n    static readonly TokenListParser<ExpressionToken, string> Not = Token.EqualTo(ExpressionToken.Not).Value(Operators.RuntimeOpNot);\n\n    static readonly TokenListParser<ExpressionToken, string> Like = Token.EqualTo(ExpressionToken.Like).Value(Operators.IntermediateOpLike);\n\n    static readonly TokenListParser<ExpressionToken, string> NotLike =\n        Token.EqualTo(ExpressionToken.Not)\n            .IgnoreThen(Token.EqualTo(ExpressionToken.Like))\n            .Value(Operators.IntermediateOpNotLike);\n\n    static readonly TokenListParser<ExpressionToken, string> In = Token.EqualTo(ExpressionToken.In).Value(Operators.RuntimeOpIn);\n\n    static readonly TokenListParser<ExpressionToken, string> NotIn =\n        Token.EqualTo(ExpressionToken.Not)\n            .IgnoreThen(Token.EqualTo(ExpressionToken.In))\n            .Value(Operators.RuntimeOpNotIn);\n\n    static readonly TokenListParser<ExpressionToken, Func<Expression, Expression>> PropertyPathStep =\n        Token.EqualTo(ExpressionToken.Period)\n            .IgnoreThen(Token.EqualTo(ExpressionToken.Identifier))\n            .Then(n => Parse.Return<ExpressionToken, Func<Expression, Expression>>(r => new AccessorExpression(r, n.ToStringValue())));\n\n    static readonly TokenListParser<ExpressionToken, Expression> Wildcard =\n        Token.EqualTo(ExpressionToken.QuestionMark).Value((Expression)new IndexerWildcardExpression(IndexerWildcard.Any))\n            .Or(Token.EqualTo(ExpressionToken.Asterisk).Value((Expression)new IndexerWildcardExpression(IndexerWildcard.All)));\n\n    static readonly TokenListParser<ExpressionToken, Func<Expression, Expression>> PropertyPathIndexerStep =\n        from open in Token.EqualTo(ExpressionToken.LBracket)\n        from indexer in Wildcard.Or(Parse.Ref(() => Expr!))\n        from close in Token.EqualTo(ExpressionToken.RBracket)\n        select new Func<Expression, Expression>(r => new IndexerExpression(r, indexer));\n\n    static readonly TokenListParser<ExpressionToken, Expression> Function =\n        (from name in Token.EqualTo(ExpressionToken.Identifier)\n            from lparen in Token.EqualTo(ExpressionToken.LParen)\n            from expr in Parse.Ref(() => Expr!).ManyDelimitedBy(Token.EqualTo(ExpressionToken.Comma))\n            from rparen in Token.EqualTo(ExpressionToken.RParen)\n            from ci in Token.EqualTo(ExpressionToken.CI).Value(true).OptionalOrDefault()\n            select (Expression)new CallExpression(ci, name.ToStringValue(), expr)).Named(\"function\");\n\n    static readonly TokenListParser<ExpressionToken, Element> ArrayElement =\n        Token.EqualTo(ExpressionToken.Spread)\n            .IgnoreThen(Parse.Ref(() => Expr!))\n            .Select(content => (Element)new SpreadElement(content))\n            .Or(Parse.Ref(() => Expr!).Select(item => (Element) new ItemElement(item)));\n\n    static readonly TokenListParser<ExpressionToken, Expression> ArrayLiteral =\n        (from lbracket in Token.EqualTo(ExpressionToken.LBracket)\n            from elements in ArrayElement.ManyDelimitedBy(Token.EqualTo(ExpressionToken.Comma))\n            from rbracket in Token.EqualTo(ExpressionToken.RBracket)\n            select (Expression)new ArrayExpression(elements)).Named(\"array\");\n\n    static readonly TokenListParser<ExpressionToken, Member> IdentifierMember =\n        from key in Token.EqualTo(ExpressionToken.Identifier).Or(Token.EqualTo(ExpressionToken.BuiltInIdentifier))\n        from value in Token.EqualTo(ExpressionToken.Colon)\n            .IgnoreThen(Parse.Ref(() => Expr!))\n            .Cast<ExpressionToken, Expression, Expression?>()\n            .OptionalOrDefault()\n        select (Member) new PropertyMember(\n            key.ToStringValue(),\n            value ?? (key.Kind == ExpressionToken.BuiltInIdentifier ?\n                new AmbientNameExpression(key.ToStringValue().Substring(1), true) :\n                new AmbientNameExpression(key.ToStringValue(), false)));\n\n    static readonly TokenListParser<ExpressionToken, Member> StringMember =\n        from key in Token.EqualTo(ExpressionToken.String).Apply(ExpressionTextParsers.String)\n        from colon in Token.EqualTo(ExpressionToken.Colon)\n        from value in Parse.Ref(() => Expr!)\n        select (Member)new PropertyMember(key, value);\n\n    static readonly TokenListParser<ExpressionToken, Member> SpreadMember =\n        from spread in Token.EqualTo(ExpressionToken.Spread)\n        from content in Parse.Ref(() => Expr!)\n        select (Member) new SpreadMember(content);\n\n    static readonly TokenListParser<ExpressionToken, Member> ObjectMember =\n        IdentifierMember.Or(StringMember).Or(SpreadMember).Named(\"object member\");\n\n    static readonly TokenListParser<ExpressionToken, Expression> ObjectLiteral =\n        (from lbrace in Token.EqualTo(ExpressionToken.LBrace)\n            from members in ObjectMember.ManyDelimitedBy(Token.EqualTo(ExpressionToken.Comma))\n            from rbrace in Token.EqualTo(ExpressionToken.RBrace)\n            select (Expression)new ObjectExpression(members)).Named(\"object\");\n\n    static readonly TokenListParser<ExpressionToken, Expression> RootProperty =\n        (from notFunction in Parse.Not(Token.EqualTo(ExpressionToken.Identifier).IgnoreThen(Token.EqualTo(ExpressionToken.LParen)))\n            from p in Token.EqualTo(ExpressionToken.BuiltInIdentifier).Select(b => (Expression) new AmbientNameExpression(b.ToStringValue().Substring(1), true))\n                .Or(Token.EqualTo(ExpressionToken.Identifier).Select(t => (Expression) new AmbientNameExpression(t.ToStringValue(), false)))\n            select p).Named(\"property\");\n\n    static readonly TokenListParser<ExpressionToken, Expression> String =\n        Token.EqualTo(ExpressionToken.String)\n            .Apply(ExpressionTextParsers.String)\n            .Select(s => (Expression)new ConstantExpression(new ScalarValue(s)));\n\n    static readonly TokenListParser<ExpressionToken, Expression> HexNumber =\n        Token.EqualTo(ExpressionToken.HexNumber)\n            .Apply(ExpressionTextParsers.HexInteger)\n            .SelectCatch(n => ulong.Parse(n, NumberStyles.HexNumber, CultureInfo.InvariantCulture), \"the numeric literal is too large\")\n            .Select(u => (Expression)new ConstantExpression(new ScalarValue((decimal)u)));\n\n    static readonly TokenListParser<ExpressionToken, Expression> Number =\n        Token.EqualTo(ExpressionToken.Number)\n            .Apply(ExpressionTextParsers.Real)\n            .SelectCatch(n => decimal.Parse(n.ToStringValue(), CultureInfo.InvariantCulture), \"the numeric literal is too large\")\n            .Select(d => (Expression)new ConstantExpression(new ScalarValue(d)));\n\n    static readonly TokenListParser<ExpressionToken, Expression> Conditional =\n        from _ in Token.EqualTo(ExpressionToken.If)\n        from condition in Parse.Ref(() => Expr!)\n        from __ in Token.EqualTo(ExpressionToken.Then)\n        from consequent in Parse.Ref(() => Expr!)\n        from ___ in Token.EqualTo(ExpressionToken.Else)\n        from alternative in Parse.Ref(() => Expr!)\n        select (Expression)new CallExpression(false, Operators.RuntimeOpIfThenElse, condition, consequent, alternative);\n\n    static readonly TokenListParser<ExpressionToken, Expression> Literal =\n        String\n            .Or(Number)\n            .Or(HexNumber)\n            .Or(Token.EqualTo(ExpressionToken.True).Value((Expression)new ConstantExpression(new ScalarValue(true))))\n            .Or(Token.EqualTo(ExpressionToken.False).Value((Expression)new ConstantExpression(new ScalarValue(false))))\n            .Or(Token.EqualTo(ExpressionToken.Null).Value((Expression)new ConstantExpression(new ScalarValue(null))))\n            .Named(\"literal\");\n\n    static readonly TokenListParser<ExpressionToken, Expression> Item =\n        Literal\n            .Or(RootProperty)\n            .Or(Function)\n            .Or(ArrayLiteral)\n            .Or(ObjectLiteral)\n            .Or(Conditional);\n\n    static readonly TokenListParser<ExpressionToken, Expression> Factor =\n        (from lparen in Token.EqualTo(ExpressionToken.LParen)\n            from expr in Parse.Ref(() => Expr!)\n            from rparen in Token.EqualTo(ExpressionToken.RParen)\n            select expr)\n        .Or(Item);\n\n    static readonly TokenListParser<ExpressionToken, Expression> Path =\n        from root in Factor\n        from path in PropertyPathStep.Or(PropertyPathIndexerStep).Many()\n        select path.Aggregate(root, (o, f) => f(o));\n\n    static readonly TokenListParser<ExpressionToken, Expression> Operand =\n        (from op in Negate.Or(Not)\n            from path in Path\n            select MakeUnary(op, path))\n        .Or(Path)\n        .Then(operand => Token.EqualTo(ExpressionToken.Is).Try()\n            .IgnoreThen(\n                Token.EqualTo(ExpressionToken.Null).Value(Operators.RuntimeOpIsNull)\n                    .Or(Token.EqualTo(ExpressionToken.Not).IgnoreThen(Token.EqualTo(ExpressionToken.Null)).Value(Operators.RuntimeOpIsNotNull)))\n            .Select(op => (Expression)new CallExpression(false, op, operand))\n            .OptionalOrDefault(operand))\n        .Named(\"expression\");\n\n    static readonly TokenListParser<ExpressionToken, Expression> InnerTerm = Parse.Chain(Power, Operand, MakeBinary);\n\n    static readonly TokenListParser<ExpressionToken, Expression> Term = Parse.Chain(Multiply.Or(Divide).Or(Modulo), InnerTerm, MakeBinary);\n\n    static readonly TokenListParser<ExpressionToken, Expression> Comparand = Parse.Chain(Add.Or(Subtract), Term, MakeBinary);\n\n    static readonly TokenListParser<ExpressionToken, Expression> Comparison = Combinators.ChainModified(\n        NotLike.Try().Or(Like)\n            .Or(NotIn.Try().Or(In))\n            .Or(Lte.Or(Neq).Or(Lt))\n            .Or(Gte.Or(Gt))\n            .Or(Eq),\n        Comparand,\n        Token.EqualTo(ExpressionToken.CI).Value(true).OptionalOrDefault(),\n        (o, l, r, ci) => new CallExpression(ci, o, l, r));\n\n    static readonly TokenListParser<ExpressionToken, Expression> Conjunction = Parse.Chain(And, Comparison, MakeBinary);\n\n    static readonly TokenListParser<ExpressionToken, Expression> Disjunction = Parse.Chain(Or, Conjunction, MakeBinary);\n\n    public static readonly TokenListParser<ExpressionToken, Expression> Expr = Disjunction;\n\n    static Expression MakeBinary(string operatorName, Expression leftOperand, Expression rightOperand)\n    {\n        return new CallExpression(false, operatorName, leftOperand, rightOperand);\n    }\n\n    static Expression MakeUnary(string operatorName, Expression operand)\n    {\n        return new CallExpression(false, operatorName, operand);\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Parsing/ExpressionTokenizer.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.ParserConstruction;\nusing Serilog.ParserConstruction.Model;\n\nnamespace Serilog.Expressions.Parsing;\n\nclass ExpressionTokenizer : Tokenizer<ExpressionToken>\n{\n    readonly ExpressionToken[] _singleCharOps = new ExpressionToken[128];\n\n    readonly ExpressionKeyword[] _keywords =\n    [\n        new(\"and\", ExpressionToken.And),\n        new(\"in\", ExpressionToken.In),\n        new(\"is\", ExpressionToken.Is),\n        new(\"like\", ExpressionToken.Like),\n        new(\"not\", ExpressionToken.Not),\n        new(\"or\", ExpressionToken.Or),\n        new(\"true\", ExpressionToken.True),\n        new(\"false\", ExpressionToken.False),\n        new(\"null\", ExpressionToken.Null),\n        new(\"if\", ExpressionToken.If),\n        new(\"then\", ExpressionToken.Then),\n        new(\"else\", ExpressionToken.Else),\n        new(\"end\", ExpressionToken.End),\n        new(\"ci\", ExpressionToken.CI),\n        new(\"each\", ExpressionToken.Each),\n        new(\"delimit\", ExpressionToken.Delimit)\n    ];\n\n    public ExpressionTokenizer()\n    {\n        _singleCharOps['+'] = ExpressionToken.Plus;\n        _singleCharOps['-'] = ExpressionToken.Minus;\n        _singleCharOps['*'] = ExpressionToken.Asterisk;\n        _singleCharOps['/'] = ExpressionToken.ForwardSlash;\n        _singleCharOps['%'] = ExpressionToken.Percent;\n        _singleCharOps['^'] = ExpressionToken.Caret;\n        _singleCharOps['<'] = ExpressionToken.LessThan;\n        _singleCharOps['>'] = ExpressionToken.GreaterThan;\n        _singleCharOps['='] = ExpressionToken.Equal;\n        _singleCharOps[','] = ExpressionToken.Comma;\n        _singleCharOps['.'] = ExpressionToken.Period;\n        _singleCharOps['('] = ExpressionToken.LParen;\n        _singleCharOps[')'] = ExpressionToken.RParen;\n        _singleCharOps['{'] = ExpressionToken.LBrace;\n        _singleCharOps['}'] = ExpressionToken.RBrace;\n        _singleCharOps[':'] = ExpressionToken.Colon;\n        _singleCharOps['['] = ExpressionToken.LBracket;\n        _singleCharOps[']'] = ExpressionToken.RBracket;\n        _singleCharOps['*'] = ExpressionToken.Asterisk;\n        _singleCharOps['?'] = ExpressionToken.QuestionMark;\n    }\n\n    public TokenList<ExpressionToken> GreedyTokenize(TextSpan textSpan)\n    {\n        // Dropping error info off for now\n        return new(\n            Tokenize(textSpan)\n                .TakeWhile(r => r.HasValue)\n                .Select(r => new Token<ExpressionToken>(r.Value, r.Location.Until(r.Remainder)))\n                .ToArray());\n    }\n\n    public IEnumerable<Result<ExpressionToken>> LazyTokenize(TextSpan span)\n    {\n        return Tokenize(span);\n    }\n\n    protected override IEnumerable<Result<ExpressionToken>> Tokenize(TextSpan stringSpan)\n    {\n        var next = SkipWhiteSpace(stringSpan);\n        if (!next.HasValue)\n            yield break;\n\n        do\n        {\n            if (char.IsDigit(next.Value))\n            {\n                var hex = ExpressionTextParsers.HexInteger(next.Location);\n                if (hex.HasValue)\n                {\n                    next = hex.Remainder.ConsumeChar();\n                    yield return Result.Value(ExpressionToken.HexNumber, hex.Location, hex.Remainder);\n                }\n                else\n                {\n                    var real = ExpressionTextParsers.Real(next.Location);\n                    if (!real.HasValue)\n                        yield return Result.CastEmpty<TextSpan, ExpressionToken>(real);\n                    else\n                        yield return Result.Value(ExpressionToken.Number, real.Location, real.Remainder);\n\n                    next = real.Remainder.ConsumeChar();\n                }\n\n                if (!IsDelimiter(next))\n                {\n                    yield return Result.Empty<ExpressionToken>(next.Location, [\"digit\"]);\n                }\n            }\n            else if (next.Value == '\\'')\n            {\n                var str = ExpressionTextParsers.String(next.Location);\n                if (!str.HasValue)\n                    yield return Result.CastEmpty<string, ExpressionToken>(str);\n\n                next = str.Remainder.ConsumeChar();\n\n                yield return Result.Value(ExpressionToken.String, str.Location, str.Remainder);\n            }\n            else if (next.Value == '@')\n            {\n                var beginIdentifier = next.Location;\n                var startOfName = next.Remainder;\n                do\n                {\n                    next = next.Remainder.ConsumeChar();\n                }\n                while (next.HasValue && char.IsLetterOrDigit(next.Value));\n\n                if (next.Remainder == startOfName)\n                {\n                    yield return Result.Empty<ExpressionToken>(startOfName, [\"built-in identifier name\"]);\n                }\n                else\n                {\n                    yield return Result.Value(ExpressionToken.BuiltInIdentifier, beginIdentifier, next.Location);\n                }\n            }\n            else if (char.IsLetter(next.Value) || next.Value == '_')\n            {\n                var beginIdentifier = next.Location;\n                do\n                {\n                    next = next.Remainder.ConsumeChar();\n                }\n                while (next.HasValue && (char.IsLetterOrDigit(next.Value) || next.Value == '_'));\n\n                if (TryGetKeyword(beginIdentifier.Until(next.Location), out var keyword))\n                {\n                    yield return Result.Value(keyword, beginIdentifier, next.Location);\n                }\n                else\n                {\n                    yield return Result.Value(ExpressionToken.Identifier, beginIdentifier, next.Location);\n                }\n            }\n            else\n            {\n                var compoundOp = ExpressionTextParsers.CompoundOperator(next.Location);\n                if (compoundOp.HasValue)\n                {\n                    yield return Result.Value(compoundOp.Value, compoundOp.Location, compoundOp.Remainder);\n                    next = compoundOp.Remainder.ConsumeChar();\n                }\n                else if (next.Value < _singleCharOps.Length && _singleCharOps[next.Value] != ExpressionToken.None)\n                {\n                    yield return Result.Value(_singleCharOps[next.Value], next.Location, next.Remainder);\n                    next = next.Remainder.ConsumeChar();\n                }\n                else\n                {\n                    yield return Result.Empty<ExpressionToken>(next.Location);\n                    next = next.Remainder.ConsumeChar();\n                }\n            }\n\n            next = SkipWhiteSpace(next.Location);\n        } while (next.HasValue);\n    }\n\n    bool IsDelimiter(Result<char> next)\n    {\n        return !next.HasValue ||\n               char.IsWhiteSpace(next.Value) ||\n               next.Value < _singleCharOps.Length && _singleCharOps[next.Value] != ExpressionToken.None;\n    }\n\n    bool TryGetKeyword(TextSpan span, out ExpressionToken keyword)\n    {\n        foreach (var kw in _keywords)\n        {\n            if (span.EqualsValueIgnoreCase(kw.Text))\n            {\n                keyword = kw.Token;\n                return true;\n            }\n        }\n\n        keyword = ExpressionToken.None;\n        return false;\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Parsing/ParserExtensions.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.ParserConstruction;\nusing Serilog.ParserConstruction.Model;\n\nnamespace Serilog.Expressions.Parsing;\n\nstatic class ParserExtensions\n{\n    public static TokenListParser<TTokenKind, TResult> SelectCatch<TTokenKind, TArg, TResult>(this TokenListParser<TTokenKind, TArg> parser, Func<TArg, TResult> trySelector, string errorMessage)\n    {\n        if (parser == null) throw new ArgumentNullException(nameof(parser));\n        if (trySelector == null) throw new ArgumentNullException(nameof(trySelector));\n        if (errorMessage == null) throw new ArgumentNullException(nameof(errorMessage));\n\n        return input =>\n        {\n            var t = parser(input);\n            if (!t.HasValue)\n                return TokenListParserResult.CastEmpty<TTokenKind, TArg, TResult>(t);\n\n            try\n            {\n                var u = trySelector(t.Value);\n                return TokenListParserResult.Value(u, input, t.Remainder);\n            }\n            catch\n            {\n                return TokenListParserResult.Empty<TTokenKind, TResult>(input, errorMessage);\n            }\n        };\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Runtime/Coerce.cs",
    "content": "// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing System.Diagnostics;\nusing System.Diagnostics.CodeAnalysis;\nusing Serilog.Events;\n\nnamespace Serilog.Expressions.Runtime;\n\nstatic class Coerce\n{\n    static readonly Type[] NumericTypes =\n    [\n        typeof(decimal),\n        typeof(int), typeof(long), typeof(double),\n        typeof(float), typeof(uint), typeof(sbyte),\n        typeof(byte), typeof(short), typeof(ushort), typeof(ulong)\n    ];\n\n    public static bool Numeric(LogEventPropertyValue? value, out decimal numeric)\n    {\n        if (value is ScalarValue sv &&\n            sv.Value != null &&\n            NumericTypes.Contains(sv.Value.GetType()))\n        {\n            numeric = (decimal)Convert.ChangeType(sv.Value, typeof(decimal));\n            return true;\n        }\n\n        numeric = default;\n        return false;\n    }\n\n    public static bool Boolean(LogEventPropertyValue? value, out bool boolean)\n    {\n        if (value is ScalarValue sv &&\n            sv.Value is bool b)\n        {\n            boolean = b;\n            return true;\n        }\n\n        boolean = default;\n        return false;\n    }\n\n    public static bool IsTrue(LogEventPropertyValue? value)\n    {\n        return Boolean(value, out var b) && b;\n    }\n\n    public static bool String(LogEventPropertyValue? value, [NotNullWhen(true)] out string? str)\n    {\n        if (value is ScalarValue sv)\n        {\n            if (sv.Value is string s)\n            {\n                str = s;\n                return true;\n            }\n\n            if (sv.Value is Exception ex)\n            {\n                str = ex.ToString();\n                return true;\n            }\n\n            if (sv.Value?.GetType().IsEnum ?? false)\n            {\n                str = sv.Value.ToString()!;\n                return true;\n            }\n\n            if (sv.Value is ActivityTraceId traceId)\n            {\n                str = traceId.ToHexString();\n                return true;\n            }\n\n            if (sv.Value is ActivitySpanId spanId)\n            {\n                str = spanId.ToHexString();\n                return true;\n            }\n        }\n\n        str = default;\n        return false;\n    }\n\n    public static bool Predicate(LogEventPropertyValue? value,\n        [MaybeNullWhen(false)] out Func<LogEventPropertyValue, LogEventPropertyValue> predicate)\n    {\n        if (value is ScalarValue sv &&\n            sv.Value is Func<LogEventPropertyValue, LogEventPropertyValue> pred)\n        {\n            predicate = pred;\n            return true;\n        }\n\n        predicate = default;\n        return false;\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Runtime/Locals.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing System.Diagnostics.CodeAnalysis;\nusing Serilog.Events;\n\nnamespace Serilog.Expressions.Runtime;\n\n/// <summary>\n/// Named local variables. We just look them up by name. The structure is a\n/// linked list with a null terminator: most of the time expressions won't have any\n/// locals, and when they do, they'll only have one or two at a given point.\n/// </summary>\nclass Locals\n{\n    readonly Locals? _others;\n    readonly string _name;\n    readonly LogEventPropertyValue _value;\n\n    Locals(Locals? others, string name, LogEventPropertyValue value)\n    {\n        _others = others;\n        _name = name;\n        _value = value;\n    }\n\n    public static Locals Set(Locals? others, string name, LogEventPropertyValue value)\n    {\n        return new(others, name, value);\n    }\n\n    public static bool TryGetValue(Locals? locals, string name, [MaybeNullWhen(false)] out LogEventPropertyValue value)\n    {\n        while (locals != null)\n        {\n            if (name == locals._name)\n            {\n                value = locals._value;\n                return true;\n            }\n\n            locals = locals._others;\n        }\n\n        value = null;\n        return false;\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Runtime/RuntimeOperators.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing System.Reflection;\nusing Serilog.Debugging;\nusing Serilog.Events;\nusing Serilog.Expressions.Compilation.Linq;\nusing Serilog.Expressions.Runtime.Support;\nusing Serilog.Templates.Rendering;\n\n// ReSharper disable ForCanBeConvertedToForeach, InvertIf, MemberCanBePrivate.Global, UnusedMember.Global, InconsistentNaming, ReturnTypeCanBeNotNullable\n\nnamespace Serilog.Expressions.Runtime;\n\nstatic class RuntimeOperators\n{\n    static readonly LogEventPropertyValue ConstantTrue = new ScalarValue(true),\n        ConstantFalse = new ScalarValue(false);\n\n    internal static LogEventPropertyValue ScalarBoolean(bool value)\n    {\n        return value ? ConstantTrue : ConstantFalse;\n    }\n\n    public static LogEventPropertyValue? Undefined()\n    {\n        return null;\n    }\n\n    public static LogEventPropertyValue? _Internal_Add(LogEventPropertyValue? left, LogEventPropertyValue? right)\n    {\n        if (Coerce.Numeric(left, out var l) &&\n            Coerce.Numeric(right, out var r))\n            return new ScalarValue(l + r);\n\n        return default;\n    }\n\n    public static LogEventPropertyValue? _Internal_Subtract(LogEventPropertyValue? left, LogEventPropertyValue? right)\n    {\n        if (Coerce.Numeric(left, out var l) &&\n            Coerce.Numeric(right, out var r))\n            return new ScalarValue(l - r);\n\n        return default;\n    }\n\n    public static LogEventPropertyValue? _Internal_Multiply(LogEventPropertyValue? left, LogEventPropertyValue? right)\n    {\n        if (Coerce.Numeric(left, out var l) &&\n            Coerce.Numeric(right, out var r))\n            return new ScalarValue(l * r);\n\n        return default;\n    }\n\n    public static LogEventPropertyValue? _Internal_Divide(LogEventPropertyValue? left, LogEventPropertyValue? right)\n    {\n        if (Coerce.Numeric(left, out var l) &&\n            Coerce.Numeric(right, out var r) &&\n            r != 0)\n            return new ScalarValue(l / r);\n\n        return default;\n    }\n\n    public static LogEventPropertyValue? _Internal_Modulo(LogEventPropertyValue? left, LogEventPropertyValue? right)\n    {\n        if (Coerce.Numeric(left, out var l) &&\n            Coerce.Numeric(right, out var r) &&\n            r != 0)\n            return new ScalarValue(l % r);\n\n        return default;\n    }\n\n    public static LogEventPropertyValue? _Internal_Power(LogEventPropertyValue? left, LogEventPropertyValue? right)\n    {\n        if (Coerce.Numeric(left, out var l) &&\n            Coerce.Numeric(right, out var r))\n            return new ScalarValue(Math.Pow((double)l, (double)r));\n\n        return default;\n    }\n\n    // ReSharper disable once ReturnTypeCanBeNotNullable\n    public static LogEventPropertyValue? _Internal_And(LogEventPropertyValue? left, LogEventPropertyValue? right)\n    {\n        throw new InvalidOperationException(\"Logical operators should be evaluated intrinsically.\");\n    }\n\n    // ReSharper disable once ReturnTypeCanBeNotNullable\n    public static LogEventPropertyValue? _Internal_Or(LogEventPropertyValue? left, LogEventPropertyValue? right)\n    {\n        throw new InvalidOperationException(\"Logical operators should be evaluated intrinsically.\");\n    }\n\n    public static LogEventPropertyValue? _Internal_LessThanOrEqual(LogEventPropertyValue? left, LogEventPropertyValue? right)\n    {\n        if (Coerce.Numeric(left, out var l) &&\n            Coerce.Numeric(right, out var r))\n            return ScalarBoolean(l <= r);\n\n        return default;\n    }\n\n    public static LogEventPropertyValue? _Internal_LessThan(LogEventPropertyValue? left, LogEventPropertyValue? right)\n    {\n        if (Coerce.Numeric(left, out var l) &&\n            Coerce.Numeric(right, out var r))\n            return ScalarBoolean(l < r);\n\n        return default;\n    }\n\n    public static LogEventPropertyValue? _Internal_GreaterThan(LogEventPropertyValue? left, LogEventPropertyValue? right)\n    {\n        if (Coerce.Numeric(left, out var l) &&\n            Coerce.Numeric(right, out var r))\n            return ScalarBoolean(l > r);\n\n        return default;\n    }\n\n    public static LogEventPropertyValue? _Internal_GreaterThanOrEqual(LogEventPropertyValue? left, LogEventPropertyValue? right)\n    {\n        if (Coerce.Numeric(left, out var l) &&\n            Coerce.Numeric(right, out var r))\n            return ScalarBoolean(l >= r);\n\n        return default;\n    }\n\n    public static LogEventPropertyValue? _Internal_Equal(StringComparison sc, LogEventPropertyValue? left, LogEventPropertyValue? right)\n    {\n        // Undefined values propagate through comparisons\n        if (left == null || right == null)\n            return null;\n\n        return ScalarBoolean(UnboxedEqualHelper(sc, left, right));\n    }\n\n    // Return value is a regular `bool` and not a scalar value as you'd get from `Equal`\n    static bool UnboxedEqualHelper(StringComparison sc, LogEventPropertyValue? left, LogEventPropertyValue? right)\n    {\n        if (left == null || right == null)\n            throw new ArgumentException(\"Undefined values should short-circuit.\");\n\n        if (Coerce.Numeric(left, out var l) &&\n            Coerce.Numeric(right, out var r))\n            return l == r;\n\n        if (Coerce.String(left, out var ls) &&\n            Coerce.String(right, out var rs))\n            return ls.Equals(rs, sc);\n\n        if (left is ScalarValue sl &&\n            right is ScalarValue sr)\n            return sl.Value?.Equals(sr.Value) ?? sr.Value == null;\n\n        if (left is SequenceValue ql &&\n            right is SequenceValue qr)\n        {\n            // Not in any way optimized :-)\n            return ql.Elements.Count == qr.Elements.Count &&\n                   ql.Elements.Zip(qr.Elements, (ll, rr) => UnboxedEqualHelper(sc, ll, rr)).All(eq => eq);\n        }\n\n        if (left is StructureValue tl &&\n            right is StructureValue tr)\n        {\n            // .... even less optimized; lots of work to de-dup keys with last-in-wins precedence.\n            var lhs = new Dictionary<string, LogEventPropertyValue?>();\n            foreach (var property in tl.Properties)\n                lhs[property.Name] = property.Value;\n\n            var rhs = new Dictionary<string, LogEventPropertyValue?>();\n            foreach (var property in tr.Properties)\n                rhs[property.Name] = property.Value;\n\n            return lhs.Keys.Count == rhs.Keys.Count &&\n                   lhs.All(kv => rhs.TryGetValue(kv.Key, out var value) &&\n                                 UnboxedEqualHelper(sc, kv.Value, value));\n        }\n\n        return false;\n    }\n\n    public static LogEventPropertyValue? _Internal_In(StringComparison sc, LogEventPropertyValue? item, LogEventPropertyValue? collection)\n    {\n        if (item == null)\n            return null;\n\n        if (collection is SequenceValue arr)\n        {\n            for (var i = 0; i < arr.Elements.Count; ++i)\n            {\n                var element = arr.Elements[i];\n                // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract\n                if (element != null && UnboxedEqualHelper(sc, element, item))\n                    return ConstantTrue;\n            }\n\n            return ConstantFalse;\n        }\n\n        return null;\n    }\n\n    public static LogEventPropertyValue? _Internal_NotIn(StringComparison sc, LogEventPropertyValue? item, LogEventPropertyValue? collection)\n    {\n        return _Internal_StrictNot(_Internal_In(sc, item, collection));\n    }\n\n    public static LogEventPropertyValue? _Internal_NotEqual(StringComparison sc, LogEventPropertyValue? left, LogEventPropertyValue? right)\n    {\n        if (left == null || right == null)\n            return null;\n\n        return ScalarBoolean(!UnboxedEqualHelper(sc, left, right));\n    }\n\n    public static LogEventPropertyValue? _Internal_Negate(LogEventPropertyValue? value)\n    {\n        if (Coerce.Numeric(value, out var numeric))\n            return new ScalarValue(-numeric);\n        return null;\n    }\n\n    public static LogEventPropertyValue? Round(LogEventPropertyValue? value, LogEventPropertyValue? places)\n    {\n        if (!Coerce.Numeric(value, out var v) ||\n            !Coerce.Numeric(places, out var p) ||\n            p is < 0 or > 32) // Check my memory, here :D\n        {\n            return null;\n        }\n\n        return new ScalarValue(Math.Round(v, (int)p));\n    }\n\n    public static LogEventPropertyValue? _Internal_Not(LogEventPropertyValue? value)\n    {\n        if (value is null)\n            return ConstantTrue;\n\n        return Coerce.Boolean(value, out var b) ?\n            ScalarBoolean(!b) :\n            null;\n    }\n\n    public static LogEventPropertyValue? _Internal_StrictNot(LogEventPropertyValue? value)\n    {\n        return Coerce.Boolean(value, out var b) ?\n            ScalarBoolean(!b) :\n            null;\n    }\n\n    public static LogEventPropertyValue? Contains(StringComparison sc, LogEventPropertyValue? haystack, LogEventPropertyValue? needle)\n    {\n        if (!Coerce.String(haystack, out var ctx) ||\n            !Coerce.String(needle, out var ptx))\n            return null;\n\n        return ScalarBoolean(ctx.Contains(ptx, sc));\n    }\n\n    public static LogEventPropertyValue? IndexOf(StringComparison sc, LogEventPropertyValue? haystack, LogEventPropertyValue? needle)\n    {\n        if (!Coerce.String(haystack, out var ctx) ||\n            !Coerce.String(needle, out var ptx))\n            return null;\n\n        return new ScalarValue(ctx.IndexOf(ptx, sc));\n    }\n\n    public static LogEventPropertyValue? LastIndexOf(StringComparison sc, LogEventPropertyValue? haystack, LogEventPropertyValue? needle)\n    {\n        if (!Coerce.String(haystack, out var ctx) ||\n            !Coerce.String(needle, out var ptx))\n            return null;\n\n        return new ScalarValue(ctx.LastIndexOf(ptx, sc));\n    }\n\n    public static LogEventPropertyValue? Length(LogEventPropertyValue? value)\n    {\n        if (Coerce.String(value, out var s))\n            return new ScalarValue(s.Length);\n\n        if (value is SequenceValue seq)\n            return new ScalarValue(seq.Elements.Count);\n\n        return null;\n    }\n\n    public static LogEventPropertyValue? StartsWith(StringComparison sc, LogEventPropertyValue? haystack, LogEventPropertyValue? needle)\n    {\n        if (!Coerce.String(haystack, out var ctx) ||\n            !Coerce.String(needle, out var ptx))\n            return null;\n\n        return ScalarBoolean(ctx.StartsWith(ptx, sc));\n    }\n\n    public static LogEventPropertyValue? EndsWith(StringComparison sc, LogEventPropertyValue? haystack, LogEventPropertyValue? needle)\n    {\n        if (!Coerce.String(haystack, out var ctx) ||\n            !Coerce.String(needle, out var ptx))\n            return null;\n\n        return ScalarBoolean(ctx.EndsWith(ptx, sc));\n    }\n\n    public static LogEventPropertyValue IsDefined(LogEventPropertyValue? value)\n    {\n        return ScalarBoolean(value != null);\n    }\n\n    public static LogEventPropertyValue? ElementAt(StringComparison sc, LogEventPropertyValue? collection, LogEventPropertyValue? index)\n    {\n        // ReSharper disable once ConvertIfStatementToSwitchStatement\n        if (collection is SequenceValue arr && Coerce.Numeric(index, out var ix))\n        {\n            if (ix != Math.Floor(ix))\n                return null;\n\n            var idx = (int)ix;\n            if (idx >= arr.Elements.Count)\n                return null;\n\n            return arr.Elements.ElementAt(idx);\n        }\n\n        if (collection is StructureValue st && Coerce.String(index, out var s))\n        {\n            return Intrinsics.TryGetStructurePropertyValue(sc, st, s);\n        }\n\n        if (collection is DictionaryValue dict && index is ScalarValue sv)\n        {\n            // The lack of eager numeric type coercion means that here, `sv` may logically equal one\n            // of the keys, but not be equal according to the dictionary's `IEqualityComparer`.\n            // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract\n            var entry = dict.Elements.FirstOrDefault(kv => kv.Key != null && UnboxedEqualHelper(sc, kv.Key, sv));\n            return entry.Value; // KVP is a struct; default is a pair of nulls.\n        }\n\n        return null;\n    }\n\n    public static LogEventPropertyValue? _Internal_Any(LogEventPropertyValue? collection, LogEventPropertyValue? predicate)\n    {\n        if (!Coerce.Predicate(predicate, out var pred))\n            return null;\n\n        if (collection is SequenceValue arr)\n        {\n            return ScalarBoolean(arr.Elements.Any(e => Coerce.IsTrue(pred(e))));\n        }\n\n        if (collection is StructureValue structure)\n        {\n            return ScalarBoolean(structure.Properties.Any(e => Coerce.IsTrue(pred(e.Value))));\n        }\n\n        if (collection is DictionaryValue dictionary)\n        {\n            return ScalarBoolean(dictionary.Elements.Any(e => Coerce.IsTrue(pred(e.Value))));\n        }\n\n        return null;\n    }\n\n    public static LogEventPropertyValue? _Internal_All(LogEventPropertyValue? collection, LogEventPropertyValue? predicate)\n    {\n        if (!Coerce.Predicate(predicate, out var pred))\n            return null;\n\n        if (collection is SequenceValue arr)\n        {\n            return ScalarBoolean(arr.Elements.All(e => Coerce.IsTrue(pred(e))));\n        }\n\n        if (collection is StructureValue structure)\n        {\n            return ScalarBoolean(structure.Properties.All(e => Coerce.IsTrue(pred(e.Value))));\n        }\n\n        if (collection is DictionaryValue dictionary)\n        {\n            return ScalarBoolean(dictionary.Elements.All(e => Coerce.IsTrue(pred(e.Value))));\n        }\n\n        return null;\n    }\n\n    public static LogEventPropertyValue? TagOf(LogEventPropertyValue? value)\n    {\n        if (value is StructureValue structure)\n            return new ScalarValue(structure.TypeTag); // I.e. may be null\n\n        return null;\n    }\n\n    public static LogEventPropertyValue TypeOf(LogEventPropertyValue? value)\n    {\n        if (value is DictionaryValue)\n            return new ScalarValue(\"dictionary\");\n\n        if (value is StructureValue)\n            return new ScalarValue(\"object\");\n\n        if (value is SequenceValue)\n            return new ScalarValue(\"array\");\n\n        if (value is ScalarValue scalar)\n        {\n            return new ScalarValue(scalar.Value?.GetType().ToString() ?? \"null\");\n        }\n\n        return new ScalarValue(\"undefined\");\n    }\n\n    public static LogEventPropertyValue _Internal_IsNull(LogEventPropertyValue? value)\n    {\n        return ScalarBoolean(value is null or ScalarValue {Value: null});\n    }\n\n    public static LogEventPropertyValue _Internal_IsNotNull(LogEventPropertyValue? value)\n    {\n        return ScalarBoolean(value is not (null or ScalarValue {Value: null}));\n    }\n\n    // Ideally this will be compiled as a short-circuiting intrinsic\n    public static LogEventPropertyValue? Coalesce(LogEventPropertyValue? value0, LogEventPropertyValue? value1)\n    {\n        if (value0 is null or ScalarValue {Value: null})\n            return value1;\n\n        return value0;\n    }\n\n    public static LogEventPropertyValue? Substring(LogEventPropertyValue? value, LogEventPropertyValue? startIndex, LogEventPropertyValue? length = null)\n    {\n        if (!Coerce.String(value, out var str) ||\n            !Coerce.Numeric(startIndex, out var si))\n            return null;\n\n        if (si < 0 || si >= str.Length || (int)si != si)\n            return null;\n\n        if (length == null)\n            return new ScalarValue(str.Substring((int)si));\n\n        if (!Coerce.Numeric(length, out var len) || (int)len != len)\n            return null;\n\n        if (len + si > str.Length)\n            return new ScalarValue(str.Substring((int)si));\n\n        return new ScalarValue(str.Substring((int)si, (int)len));\n    }\n\n    public static LogEventPropertyValue? Concat(LogEventPropertyValue? string0, LogEventPropertyValue? string1)\n    {\n        if (Coerce.String(string0, out var f) && Coerce.String(string1, out var s))\n        {\n            return new ScalarValue(f + s);\n        }\n\n        return null;\n    }\n\n    public static LogEventPropertyValue? Replace(StringComparison sc, LogEventPropertyValue? haystack, LogEventPropertyValue? needle, LogEventPropertyValue? replacement)\n    {\n        if (Coerce.String(haystack, out var h) && Coerce.String(needle, out var n) && Coerce.String(replacement, out var r))\n        {\n            return new ScalarValue(h.Replace(n, r, sc));\n        }\n\n        return null;\n    }\n\n    // ReSharper disable once ReturnTypeCanBeNotNullable\n    public static LogEventPropertyValue? IndexOfMatch(StringComparison sc, LogEventPropertyValue? haystack, LogEventPropertyValue? needle)\n    {\n        throw new InvalidOperationException(\"Regular expression evaluation is intrinsic.\");\n    }\n\n    // ReSharper disable once ReturnTypeCanBeNotNullable\n    public static LogEventPropertyValue? IsMatch(StringComparison sc, LogEventPropertyValue? haystack, LogEventPropertyValue? needle)\n    {\n        throw new InvalidOperationException(\"Regular expression evaluation is intrinsic.\");\n    }\n\n    // Ideally this will be compiled as a short-circuiting intrinsic\n    public static LogEventPropertyValue? _Internal_IfThenElse(\n        LogEventPropertyValue? condition,\n        LogEventPropertyValue? consequent,\n        LogEventPropertyValue? alternative)\n    {\n        return Coerce.IsTrue(condition) ? consequent : alternative;\n    }\n\n    public static LogEventPropertyValue? ToString(IFormatProvider? formatProvider, LogEventPropertyValue? value, LogEventPropertyValue? format = null)\n    {\n        if (value is not ScalarValue sv ||\n            sv.Value == null ||\n            !(Coerce.String(format, out var fmt) || format is null or ScalarValue { Value: null }))\n        {\n            return null;\n        }\n\n        var toString = sv.Value switch\n        {\n            LogEventLevel level => LevelRenderer.GetLevelMoniker(level, fmt),\n            IFormattable formattable => formattable.ToString(fmt, formatProvider),\n            _ => sv.Value.ToString()\n        };\n\n        return new ScalarValue(toString);\n    }\n\n    public static LogEventPropertyValue? UtcDateTime(LogEventPropertyValue? dateTime)\n    {\n        if (dateTime is ScalarValue sv)\n        {\n            if (sv.Value is DateTimeOffset dto)\n                return new ScalarValue(dto.UtcDateTime);\n\n            if (sv.Value is DateTime dt)\n                return new ScalarValue(dt.ToUniversalTime());\n        }\n\n        return null;\n    }\n\n    // ReSharper disable once UnusedMember.Global\n    public static LogEventPropertyValue? Now()\n    {\n        // DateTimeOffset.Now is the generator for LogEvent.Timestamp.\n        return new ScalarValue(DateTimeOffset.Now);\n    }\n\n    public static LogEventPropertyValue? Inspect(LogEventPropertyValue? value, LogEventPropertyValue? deep = null)\n    {\n        if (value is not ScalarValue { Value: {} toCapture })\n            return value;\n\n        var result = new List<LogEventProperty>();\n        var logger = new LoggerConfiguration().CreateLogger();\n        var properties = toCapture.GetType()\n            .GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty);\n\n        foreach (var property in properties)\n        {\n            object? p;\n            try\n            {\n                p = property.GetValue(toCapture);\n            }\n            catch (Exception ex)\n            {\n                SelfLog.WriteLine(\"Serilog.Expressions Inspect() target property threw exception: {0}\", ex);\n                continue;\n            }\n            \n            if (deep is ScalarValue { Value: true })\n            {\n                if (logger.BindProperty(property.Name, p, destructureObjects: true, out var bound))\n                    result.Add(bound);\n            }\n            else\n            {\n                result.Add(new LogEventProperty(property.Name, new ScalarValue(p)));\n            }\n        }\n\n        return new StructureValue(result);\n    }\n\n    public static LogEventPropertyValue? Nest(LogEventPropertyValue? maybeStructure)\n    {\n        if (maybeStructure is not StructureValue { Properties: { } flat })\n            return null;\n\n        var byName = new Dictionary<string, LogEventPropertyValue>(flat.Count);\n        foreach (var property in flat)\n        {\n            // Supports duplicate property names, despite these being hard to generate.\n            byName[property.Name] = property.Value;\n        }\n        \n        var props = UnflattenDottedPropertyNames.ProcessDottedPropertyNames(byName);\n        return new StructureValue(props.Select(p => new LogEventProperty(p.Key, p.Value)).ToList());\n    }\n}\n"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/Runtime/Support/UnflattenDottedPropertyNames.cs",
    "content": "// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing System.Diagnostics;\nusing Serilog.Events;\n\nnamespace Serilog.Expressions.Runtime.Support;\n\n/// <summary>\n/// Nest (un-flatten) properties with dotted names. A property with name <c>\"a.b\"</c> will be transmitted to Seq as\n/// a structure with name <c>\"a\"</c>, and one member <c>\"b\"</c>.\n/// </summary>\n/// <remarks>Forked from <a href=\"https://github.com/datalust/serilog-sinks-seq/blob/6815d7307b477747e05e782f7dfbcff8c8dd20a2/src/Serilog.Sinks.Seq/Sinks/Seq/Conventions/UnflattenDottedPropertyNames.cs\"/>.</remarks>\nstatic class UnflattenDottedPropertyNames\n{\n    const int MaxDepth = 10;\n\n    public static IReadOnlyDictionary<string, LogEventPropertyValue> ProcessDottedPropertyNames(IReadOnlyDictionary<string, LogEventPropertyValue> maybeDotted)\n    {\n        return DottedToNestedRecursive(maybeDotted, 0);\n    }\n\n    static IReadOnlyDictionary<string, LogEventPropertyValue> DottedToNestedRecursive(IReadOnlyDictionary<string, LogEventPropertyValue> maybeDotted, int depth)\n    {\n        if (depth == MaxDepth)\n            return maybeDotted;\n\n        // Assume that the majority of entries will be bare or have unique prefixes.\n        var result = new Dictionary<string, LogEventPropertyValue>(maybeDotted.Count);\n\n        // Sorted for determinism.\n        var dotted = new SortedDictionary<string, LogEventPropertyValue>(StringComparer.Ordinal);\n\n        // First - give priority to bare names, since these would otherwise be claimed by the parents of further nested\n        // layers and we'd have nowhere to put them when resolving conflicts. (Dotted entries that conflict can keep their dotted keys).\n\n        foreach (var kv in maybeDotted)\n        {\n            if (IsDottedIdentifier(kv.Key))\n            {\n                // Stash for processing in the next stage.\n                dotted.Add(kv.Key, kv.Value);\n            }\n            else\n            {\n                result.Add(kv.Key, kv.Value);\n            }\n        }\n\n        // Then - for dotted keys with a prefix not already present in the result, convert to structured data and add to\n        // the result. Any set of dotted names that collide with a preexisting key will be left as-is.\n\n        string? prefix = null;\n        Dictionary<string, LogEventPropertyValue>? nested = null;\n        foreach (var kv in dotted)\n        {\n            var (newPrefix, rem) = TakeFirstIdentifier(kv.Key);\n\n            if (prefix != null && prefix != newPrefix)\n            {\n                result.Add(prefix, MakeStructureValue(DottedToNestedRecursive(nested!, depth + 1)));\n                prefix = null;\n                nested = null;\n            }\n\n            if (nested != null && !nested.ContainsKey(rem))\n            {\n                prefix = newPrefix;\n                nested.Add(rem, kv.Value);\n            }\n            else if (nested == null && !result.ContainsKey(newPrefix))\n            {\n                prefix = newPrefix;\n                nested = new () { { rem, kv.Value } };\n            }\n            else\n            {\n                result.Add(kv.Key, kv.Value);\n            }\n        }\n\n        if (prefix != null)\n        {\n            result[prefix] = MakeStructureValue(DottedToNestedRecursive(nested!, depth + 1));\n        }\n\n        return result;\n    }\n\n    static StructureValue MakeStructureValue(IReadOnlyDictionary<string,LogEventPropertyValue> properties)\n    {\n        return new StructureValue(properties.Select(kv => new LogEventProperty(kv.Key, kv.Value)), typeTag: null);\n    }\n\n    static bool IsDottedIdentifier(string key) =>\n        key.Contains('.') &&\n        !key.StartsWith(\".\", StringComparison.Ordinal) &&\n        !key.EndsWith(\".\", StringComparison.Ordinal) &&\n        key.Split('.').All(IsIdentifier);\n\n    static bool IsIdentifier(string s) => s.Length != 0 &&\n                                          !char.IsDigit(s[0]) &&\n                                          s.All(ch => char.IsLetter(ch) || char.IsDigit(ch) || ch == '_');\n\n    static (string, string) TakeFirstIdentifier(string dottedIdentifier)\n    {\n        // We can do this simplistically because keys in `dotted` conform to `IsDottedName`.\n        Debug.Assert(IsDottedIdentifier(dottedIdentifier));\n\n        var firstDot = dottedIdentifier.IndexOf('.');\n        var prefix = dottedIdentifier.Substring(0, firstDot);\n        var rem = dottedIdentifier.Substring(firstDot + 1);\n        return (prefix, rem);\n    }\n}\n"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/SerilogExpression.cs",
    "content": "﻿// Copyright Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing System.Diagnostics.CodeAnalysis;\nusing System.Globalization;\nusing Serilog.Expressions.Compilation;\nusing Serilog.Expressions.Parsing;\n\n// ReSharper disable MemberCanBePrivate.Global\n\nnamespace Serilog.Expressions;\n\n/// <summary>\n/// Helper methods to assist with construction of well-formed expressions.\n/// </summary>\npublic static class SerilogExpression\n{\n    /// <summary>\n    /// Create an evaluation function based on the provided expression.\n    /// </summary>\n    /// <param name=\"expression\">An expression.</param>\n    /// <param name=\"formatProvider\">Optionally, a format provider that will be used for culture-specific formatting;\n    ///     by default, <see cref=\"CultureInfo.CurrentCulture\"/> is used.</param>\n    /// <param name=\"nameResolver\">Optionally, a <see cref=\"NameResolver\"/>\n    ///     with which to resolve function names that appear in the template.</param>\n    /// <returns>A function that evaluates the expression in the context of a log event.</returns>\n    public static CompiledExpression Compile(string expression,\n        IFormatProvider? formatProvider = null,\n        NameResolver? nameResolver = null)\n    {\n        if (expression == null) throw new ArgumentNullException(nameof(expression));\n        if (!TryCompileImpl(expression, formatProvider, nameResolver, out var filter, out var error))\n            throw new ArgumentException(error);\n\n        return filter;\n    }\n\n    /// <summary>\n    /// Create an evaluation function based on the provided expression.\n    /// </summary>\n    /// <param name=\"expression\">An expression.</param>\n    /// <param name=\"result\">A function that evaluates the expression in the context of a log event.</param>\n    /// <param name=\"error\">The reported error, if compilation was unsuccessful.</param>\n    /// <returns>True if the function could be created; otherwise, false.</returns>\n    /// <remarks>Validation errors including invalid regular expressions and unknown function names are returned\n    /// as friendly error messages. Invalid case-insensitive modifiers are ignored with warnings.</remarks>\n    public static bool TryCompile(\n        string expression,\n        [MaybeNullWhen(false)] out CompiledExpression result,\n        [MaybeNullWhen(true)] out string error)\n    {\n        if (expression == null) throw new ArgumentNullException(nameof(expression));\n        return TryCompileImpl(expression, null, null, out result, out error);\n    }\n\n    /// <summary>\n    /// Create an evaluation function based on the provided expression.\n    /// </summary>\n    /// <param name=\"expression\">An expression.</param>\n    /// <param name=\"formatProvider\">Optionally, a format provider that will be used for culture-specific formatting;\n    ///     by default, <see cref=\"CultureInfo.CurrentCulture\"/> is used.</param>\n    /// <param name=\"nameResolver\">A <see cref=\"NameResolver\"/>\n    ///     with which to resolve function names that appear in the template.</param>\n    /// <param name=\"result\">A function that evaluates the expression in the context of a log event.</param>\n    /// <param name=\"error\">The reported error, if compilation was unsuccessful.</param>\n    /// <returns>True if the function could be created; otherwise, false.</returns>\n    /// <remarks>Validation errors including invalid regular expressions and unknown function names are returned\n    /// as friendly error messages. Invalid case-insensitive modifiers are ignored with warnings.</remarks>\n    public static bool TryCompile(string expression,\n        IFormatProvider? formatProvider,\n        NameResolver nameResolver,\n        [MaybeNullWhen(false)] out CompiledExpression result,\n        [MaybeNullWhen(true)] out string error)\n    {\n        if (expression == null) throw new ArgumentNullException(nameof(expression));\n        if (nameResolver == null) throw new ArgumentNullException(nameof(nameResolver));\n        return TryCompileImpl(expression, formatProvider, nameResolver, out result, out error);\n    }\n\n    static bool TryCompileImpl(string expression,\n        IFormatProvider? formatProvider,\n        NameResolver? nameResolver,\n        [MaybeNullWhen(false)] out CompiledExpression result,\n        [MaybeNullWhen(true)] out string error)\n    {\n        var expressionParser = new ExpressionParser();\n        if (!expressionParser.TryParse(expression, out var root, out error))\n        {\n            result = null;\n            return false;\n        }\n\n        try\n        {\n            var evaluate = ExpressionCompiler.Compile(root, formatProvider, DefaultFunctionNameResolver.Build(nameResolver));\n            result = evt => evaluate(new(evt));\n            error = null;\n            return true;\n        }\n        catch (ExpressionValidationException ex)\n        {\n            // Catch validation errors from compilation\n            result = null;\n            error = ex.Message;\n            return false;\n        }\n    }\n\n    /// <summary>\n    /// Escape a value that is to appear in a `like` expression.\n    /// </summary>\n    /// <param name=\"text\">The text to escape.</param>\n    /// <returns>The text with any special values escaped. Will need to be passed through\n    /// <see cref=\"EscapeStringContent(string)\"/> if it is being embedded directly into a filter expression.</returns>\n    // ReSharper disable once UnusedMember.Global\n    public static string EscapeLikeExpressionContent(string text)\n    {\n        if (text == null) throw new ArgumentNullException(nameof(text));\n        return EscapeStringContent(text)\n            .Replace(\"%\", \"%%\")\n            .Replace(\"_\", \"__\");\n    }\n\n    /// <summary>\n    /// Escape a fragment of text that will appear within a string.\n    /// </summary>\n    /// <param name=\"text\">The text to escape.</param>\n    /// <returns>The text with any special values escaped.</returns>\n    public static string EscapeStringContent(string text)\n    {\n        if (text == null) throw new ArgumentNullException(nameof(text));\n        return text.Replace(\"'\", \"''\");\n    }\n\n    /// <summary>\n    /// Determine if the specified text is a valid identifier.\n    /// </summary>\n    /// <param name=\"identifier\">The text to check.</param>\n    /// <returns>True if the text can be used verbatim as a property name.</returns>\n    public static bool IsValidIdentifier(string identifier)\n    {\n        return identifier.Length != 0 &&\n               !char.IsDigit(identifier[0]) &&\n               identifier.All(ch => char.IsLetter(ch) || char.IsDigit(ch) || ch == '_');\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Expressions/StaticMemberNameResolver.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing System.Diagnostics.CodeAnalysis;\nusing System.Reflection;\n\nnamespace Serilog.Expressions;\n\n/// <summary>\n/// A <see cref=\"NameResolver\"/> that matches public static members of a class by name.\n/// </summary>\npublic class StaticMemberNameResolver : NameResolver\n{\n    readonly IReadOnlyDictionary<string, MethodInfo> _methods;\n\n    /// <summary>\n    /// Create a <see cref=\"StaticMemberNameResolver\"/> that returns members of the specified <see cref=\"Type\"/>.\n    /// </summary>\n    /// <param name=\"type\">A <see cref=\"Type\"/> with public static members implementing runtime functions.</param>\n    public StaticMemberNameResolver(\n#if NET6_0_OR_GREATER\n        [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)]\n#endif\n        Type type)\n    {\n        if (type == null) throw new ArgumentNullException(nameof(type));\n\n        _methods = type\n            .GetTypeInfo()\n            .GetMethods(BindingFlags.Static | BindingFlags.Public)\n            .ToDictionary(m => m.Name, StringComparer.OrdinalIgnoreCase);\n    }\n\n    /// <inheritdoc />\n    public override bool TryResolveFunctionName(string name, [MaybeNullWhen(false)] out MethodInfo implementation)\n    {\n        return _methods.TryGetValue(name, out implementation);\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/LoggerEnrichmentConfigurationExtensions.cs",
    "content": "﻿// Copyright 2019 Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Configuration;\nusing Serilog.Expressions;\nusing Serilog.Expressions.Runtime;\nusing Serilog.Pipeline;\n\nnamespace Serilog;\n\n/// <summary>\n/// Extends logger enrichment configuration with methods for filtering with expressions.\n/// </summary>\npublic static class LoggerEnrichmentConfigurationExtensions\n{\n    /// <summary>\n    /// Write to a sink only when <paramref name=\"expression\" /> evaluates to <c>true</c>.\n    /// </summary>\n    /// <param name=\"loggerEnrichmentConfiguration\">Enrichment configuration.</param>\n    /// <param name=\"expression\">An expression that evaluates to <c>true</c> when the supplied\n    /// <see cref=\"T:Serilog.Events.LogEvent\" /> should be enriched.</param>\n    /// <param name=\"configureEnricher\">An action that configures the wrapped enricher.</param>\n    /// <returns>The underlying <see cref=\"LoggerConfiguration\"/>.</returns>\n    public static LoggerConfiguration When(\n        this LoggerEnrichmentConfiguration loggerEnrichmentConfiguration,\n        string expression,\n        Action<LoggerEnrichmentConfiguration> configureEnricher)\n    {\n        if (loggerEnrichmentConfiguration == null) throw new ArgumentNullException(nameof(loggerEnrichmentConfiguration));\n        if (expression == null) throw new ArgumentNullException(nameof(expression));\n        if (configureEnricher == null) throw new ArgumentNullException(nameof(configureEnricher));\n\n        var compiled = SerilogExpression.Compile(expression);\n        return loggerEnrichmentConfiguration.When(e => Coerce.IsTrue(compiled(e)), configureEnricher);\n    }\n\n    /// <summary>\n    /// Enrich events with a property <paramref name=\"propertyName\"/> computed by evaluating\n    /// <paramref name=\"expression\"/> in the context of the event.\n    /// </summary>\n    /// <param name=\"loggerEnrichmentConfiguration\">Enrichment configuration.</param>\n    /// <param name=\"propertyName\">The name of the property to attach; if the property already\n    /// exists, and <paramref name=\"expression\"/> evaluates to a defined value, it will be overwritten.</param>\n    /// <param name=\"expression\">An expression to evaluate in the context of each event. If the result of\n    /// evaluating the expression is defined, it will be attached to the event as <paramref name=\"propertyName\"/>.</param>\n    /// <returns>The underlying <see cref=\"LoggerConfiguration\"/>.</returns>\n    public static LoggerConfiguration WithComputed(\n        this LoggerEnrichmentConfiguration loggerEnrichmentConfiguration,\n        string propertyName,\n        string expression)\n    {\n        if (propertyName == null) throw new ArgumentNullException(nameof(propertyName));\n        if (expression == null) throw new ArgumentNullException(nameof(expression));\n        var compiled = SerilogExpression.Compile(expression);\n        return loggerEnrichmentConfiguration.With(new ComputedPropertyEnricher(propertyName, compiled));\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/LoggerFilterConfigurationExtensions.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Configuration;\nusing Serilog.Expressions;\nusing Serilog.Expressions.Runtime;\n\n// ReSharper disable UnusedMember.Global\n\nnamespace Serilog;\n\n/// <summary>\n/// Extends logger filter configuration with methods for filtering with expressions.\n/// </summary>\npublic static class LoggerFilterConfigurationExtensions\n{\n    /// <summary>\n    /// Include only log events that match the provided expression.\n    /// </summary>\n    /// <param name=\"loggerFilterConfiguration\">Filter configuration.</param>\n    /// <param name=\"expression\">The expression to apply.</param>\n    /// <returns>The underlying <see cref=\"LoggerConfiguration\"/>.</returns>\n    public static LoggerConfiguration ByIncludingOnly(this LoggerFilterConfiguration loggerFilterConfiguration, string expression)\n    {\n        if (loggerFilterConfiguration == null) throw new ArgumentNullException(nameof(loggerFilterConfiguration));\n        if (expression == null) throw new ArgumentNullException(nameof(expression));\n\n        var compiled = SerilogExpression.Compile(expression);\n        return loggerFilterConfiguration.ByIncludingOnly(e => Coerce.IsTrue(compiled(e)));\n    }\n\n    /// <summary>\n    /// Exclude log events that match the provided expression.\n    /// </summary>\n    /// <param name=\"loggerFilterConfiguration\">Filter configuration.</param>\n    /// <param name=\"expression\">The expression to apply.</param>\n    /// <returns>The underlying <see cref=\"LoggerConfiguration\"/>.</returns>\n    public static LoggerConfiguration ByExcluding(this LoggerFilterConfiguration loggerFilterConfiguration, string expression)\n    {\n        if (loggerFilterConfiguration == null) throw new ArgumentNullException(nameof(loggerFilterConfiguration));\n        if (expression == null) throw new ArgumentNullException(nameof(expression));\n\n        var compiled = SerilogExpression.Compile(expression);\n        return loggerFilterConfiguration.ByExcluding(e => Coerce.IsTrue(compiled(e)));\n    }\n\n    /// <summary>\n    /// Use a <see cref=\"LoggingFilterSwitch\"/> to dynamically control filtering.\n    /// </summary>\n    /// <param name=\"loggerFilterConfiguration\">Filter configuration.</param>\n    /// <param name=\"switch\">A <see cref=\"LoggingFilterSwitch\"/> that can be used to dynamically control\n    /// log filtering.</param>\n    /// <returns>The underlying <see cref=\"LoggerConfiguration\"/>.</returns>\n    public static LoggerConfiguration ControlledBy(this LoggerFilterConfiguration loggerFilterConfiguration, LoggingFilterSwitch @switch)\n    {\n        if (loggerFilterConfiguration == null) throw new ArgumentNullException(nameof(loggerFilterConfiguration));\n        if (@switch == null) throw new ArgumentNullException(nameof(@switch));\n\n        return loggerFilterConfiguration.With(@switch);\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/LoggerSinkConfigurationExtensions.cs",
    "content": "﻿// Copyright 2019 Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Configuration;\nusing Serilog.Expressions;\nusing Serilog.Expressions.Runtime;\n\nnamespace Serilog;\n\n/// <summary>\n/// Extends logger sink configuration with methods for filtering with expressions.\n/// </summary>\npublic static class LoggerSinkConfigurationExtensions\n{\n    /// <summary>\n    /// Write to a sink only when <paramref name=\"expression\" /> evaluates to <c>true</c>.\n    /// </summary>\n    /// <param name=\"loggerSinkConfiguration\">Sink configuration.</param>\n    /// <param name=\"expression\">An expression that evaluates to <c>true</c> when the\n    /// supplied <see cref=\"T:Serilog.Events.LogEvent\" />\n    /// should be written to the configured sink.</param>\n    /// <param name=\"configureSink\">An action that configures the wrapped sink.</param>\n    /// <returns>Configuration object allowing method chaining.</returns>\n    /// <returns>The underlying <see cref=\"LoggerConfiguration\"/>.</returns>\n    public static  LoggerConfiguration Conditional(\n        this LoggerSinkConfiguration loggerSinkConfiguration,\n        string expression,\n        Action<LoggerSinkConfiguration> configureSink)\n    {\n        if (loggerSinkConfiguration == null) throw new ArgumentNullException(nameof(loggerSinkConfiguration));\n        if (expression == null) throw new ArgumentNullException(nameof(expression));\n        if (configureSink == null) throw new ArgumentNullException(nameof(configureSink));\n\n        var compiled = SerilogExpression.Compile(expression);\n        return loggerSinkConfiguration.Conditional(e => Coerce.IsTrue(compiled(e)), configureSink);\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/ParserConstruction/Combinators.cs",
    "content": "// Copyright 2016 Datalust, Superpower Contributors, Sprache Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.ParserConstruction.Display;\nusing Serilog.ParserConstruction.Model;\nusing Serilog.ParserConstruction.Util;\n\n// ReSharper disable MemberCanBePrivate.Global\n\nnamespace Serilog.ParserConstruction;\n\n/// <summary>\n/// Functions that construct more complex parsers by combining simpler ones.\n/// </summary>\nstatic class Combinators\n{\n    /// <summary>\n    /// Apply the text parser <paramref name=\"valueParser\"/> to the span represented by the parsed token.\n    /// </summary>\n    /// <typeparam name=\"TKind\">The kind of the tokens being parsed.</typeparam>\n    /// <typeparam name=\"U\">The type of the resulting value.</typeparam>\n    /// <param name=\"parser\">The parser.</param>\n    /// <param name=\"valueParser\">A text parser to apply.</param>\n    /// <returns>A parser that returns the result of parsing the token value.</returns>\n    public static TokenListParser<TKind, U> Apply<TKind, U>(this TokenListParser<TKind, Token<TKind>> parser, TextParser<U> valueParser)\n    {\n        if (parser == null) throw new ArgumentNullException(nameof(parser));\n        if (valueParser == null) throw new ArgumentNullException(nameof(valueParser));\n\n        var valueParserAtEnd = valueParser.AtEnd();\n        return input =>\n        {\n            var rt = parser(input);\n            if (!rt.HasValue)\n                return TokenListParserResult.CastEmpty<TKind, Token<TKind>, U>(rt);\n\n            var uResult = valueParserAtEnd(rt.Value.Span);\n            if (uResult.HasValue)\n                return TokenListParserResult.Value(uResult.Value, rt.Location, rt.Remainder);\n\n            var problem = uResult.Remainder.IsAtEnd ? \"incomplete\" : \"invalid\";\n            var textError = uResult.Remainder.IsAtEnd ? uResult.Expectations != null ? $\", expected {Friendly.List(uResult.Expectations)}\" : \"\" : $\", {uResult.FormatErrorMessageFragment()}\";\n            var message = $\"{problem} {Presentation.FormatExpectation(rt.Value.Kind)}{textError}\";\n            return new(input, rt.Remainder, uResult.Remainder.Position, message, null, uResult.Backtrack);\n        };\n    }\n\n    /// <summary>\n    /// Construct a parser that succeeds only if the source is at the end of input.\n    /// </summary>\n    /// <typeparam name=\"T\">The type of value being parsed.</typeparam>\n    /// <param name=\"parser\">The parser.</param>\n    /// <returns>The resulting parser.</returns>\n    public static TextParser<T> AtEnd<T>(this TextParser<T> parser)\n    {\n        if (parser == null) throw new ArgumentNullException(nameof(parser));\n\n        return input =>\n        {\n            var result = parser(input);\n            if (!result.HasValue)\n                return result;\n\n            if (result.Remainder.IsAtEnd)\n                return result;\n\n            return Result.Empty<T>(result.Remainder);\n        };\n    }\n\n    /// <summary>\n    /// Construct a parser that succeeds only if the source is at the end of input.\n    /// </summary>\n    /// <typeparam name=\"TKind\">The kind of the tokens being parsed.</typeparam>\n    /// <typeparam name=\"T\">The type of value being parsed.</typeparam>\n    /// <param name=\"parser\">The parser.</param>\n    /// <returns>The resulting parser.</returns>\n    public static TokenListParser<TKind, T> AtEnd<TKind, T>(this TokenListParser<TKind, T> parser)\n    {\n        if (parser == null) throw new ArgumentNullException(nameof(parser));\n\n        return input =>\n        {\n            var result = parser(input);\n            if (!result.HasValue)\n                return result;\n\n            if (result.Remainder.IsAtEnd)\n                return result;\n\n            return TokenListParserResult.Empty<TKind, T>(result.Remainder);\n        };\n    }\n\n    /// <summary>\n    /// Construct a parser that matches one or more instances of applying <paramref name=\"parser\"/>.\n    /// </summary>\n    /// <typeparam name=\"T\">The type of value being parsed.</typeparam>\n    /// <param name=\"parser\">The parser.</param>\n    /// <returns>The resulting parser.</returns>\n    public static TextParser<T[]> AtLeastOnce<T>(this TextParser<T> parser)\n    {\n        if (parser == null) throw new ArgumentNullException(nameof(parser));\n        return parser.Then(first => parser.Many().Select(rest => (T[])[first, ..rest]));\n    }\n\n    /// <summary>\n    /// Construct a parser that matches one or more instances of applying <paramref name=\"parser\"/>, delimited by <paramref name=\"delimiter\"/>.\n    /// </summary>\n    /// <typeparam name=\"TKind\">The kind of the tokens being parsed.</typeparam>\n    /// <typeparam name=\"T\">The type of value being parsed.</typeparam>\n    /// <typeparam name=\"U\">The type of the resulting value.</typeparam>\n    /// <param name=\"parser\">The parser.</param>\n    /// <param name=\"delimiter\">The parser that matches the delimiters.</param>\n    /// <returns>The resulting parser.</returns>\n    public static TokenListParser<TKind, T[]> AtLeastOnceDelimitedBy<TKind, T, U>(this TokenListParser<TKind, T> parser, TokenListParser<TKind, U> delimiter)\n    {\n        if (parser == null) throw new ArgumentNullException(nameof(parser));\n        if (delimiter == null) throw new ArgumentNullException(nameof(delimiter));\n\n        return parser.Then(first => delimiter.IgnoreThen(parser).Many().Select(rest => (T[])[first, ..rest]));\n    }\n\n    /// <summary>\n    /// Construct a parser that matches <paramref name=\"first\"/>, discards the resulting value, then returns the result of <paramref name=\"second\"/>.\n    /// </summary>\n    /// <typeparam name=\"TKind\">The kind of the tokens being parsed.</typeparam>\n    /// <typeparam name=\"T\">The type of value being parsed.</typeparam>\n    /// <typeparam name=\"U\">The type of the resulting value.</typeparam>\n    /// <param name=\"first\">The first parser.</param>\n    /// <param name=\"second\">The second parser.</param>\n    /// <returns>The resulting parser.</returns>\n    public static TokenListParser<TKind, U> IgnoreThen<TKind, T, U>(this TokenListParser<TKind, T> first, TokenListParser<TKind, U> second)\n    {\n        if (first == null) throw new ArgumentNullException(nameof(first));\n        if (second == null) throw new ArgumentNullException(nameof(second));\n\n        return input =>\n        {\n            var rt = first(input);\n            if (!rt.HasValue)\n                return TokenListParserResult.CastEmpty<TKind, T, U>(rt);\n\n            var ru = second(rt.Remainder);\n            if (!ru.HasValue)\n                return ru;\n\n            return TokenListParserResult.Value(ru.Value, input, ru.Remainder);\n        };\n    }\n\n    /// <summary>\n    /// Construct a parser that matches <paramref name=\"first\"/>, discards the resulting value, then returns the result of <paramref name=\"second\"/>.\n    /// </summary>\n    /// <typeparam name=\"T\">The type of value being parsed.</typeparam>\n    /// <typeparam name=\"U\">The type of the resulting value.</typeparam>\n    /// <param name=\"first\">The first parser.</param>\n    /// <param name=\"second\">The second parser.</param>\n    /// <returns>The resulting parser.</returns>\n    public static TextParser<U> IgnoreThen<T, U>(this TextParser<T> first, TextParser<U> second)\n    {\n        if (first == null) throw new ArgumentNullException(nameof(first));\n        if (second == null) throw new ArgumentNullException(nameof(second));\n\n        return input =>\n        {\n            var rt = first(input);\n            if (!rt.HasValue)\n                return Result.CastEmpty<T, U>(rt);\n\n            var ru = second(rt.Remainder);\n            if (!ru.HasValue)\n                return ru;\n\n            return Result.Value(ru.Value, input, ru.Remainder);\n        };\n    }\n\n    /// <summary>\n    /// Construct a parser that matches <paramref name=\"parser\"/> zero or more times.\n    /// </summary>\n    /// <typeparam name=\"TKind\">The kind of the tokens being parsed.</typeparam>\n    /// <typeparam name=\"T\">The type of value being parsed.</typeparam>\n    /// <param name=\"parser\">The parser.</param>\n    /// <returns>The resulting parser.</returns>\n    /// <remarks>Many will fail if any item partially matches this. To modify this behavior use <see cref=\"Try{TKind,T}(TokenListParser{TKind,T})\"/>.</remarks>\n    public static TokenListParser<TKind, T[]> Many<TKind, T>(this TokenListParser<TKind, T> parser)\n    {\n        if (parser == null) throw new ArgumentNullException(nameof(parser));\n\n        return input =>\n        {\n            var result = new List<T>();\n            var from = input;\n            var r = parser(input);\n            while (r.HasValue)\n            {\n                if (from == r.Remainder) // Broken parser, not a failed parsing.\n                    throw new ParseException($\"Many() cannot be applied to zero-width parsers; value {r.Value} at position {r.Location.Position}.\", r.ErrorPosition);\n\n                result.Add(r.Value);\n                from = r.Remainder;\n                r = parser(r.Remainder);\n            }\n\n            if (!r.Backtrack && r.IsPartial(from))\n                return TokenListParserResult.CastEmpty<TKind, T, T[]>(r);\n\n            return TokenListParserResult.Value(result.ToArray(), input, from);\n        };\n    }\n\n    /// <summary>\n    /// Construct a parser that matches <paramref name=\"parser\"/> zero or more times.\n    /// </summary>\n    /// <typeparam name=\"T\">The type of value being parsed.</typeparam>\n    /// <param name=\"parser\">The parser.</param>\n    /// <returns>The resulting parser.</returns>\n    /// <remarks>Many will fail if any item partially matches this. To modify this behavior use <see cref=\"Try{T}(TextParser{T})\"/>.</remarks>\n    public static TextParser<T[]> Many<T>(this TextParser<T> parser)\n    {\n        if (parser == null) throw new ArgumentNullException(nameof(parser));\n\n        return input =>\n        {\n            var result = new List<T>();\n            var from = input;\n            var r = parser(input);\n            while (r.HasValue)\n            {\n                if (from == r.Remainder) // Broken parser, not a failed parsing.\n                    throw new ParseException($\"Many() cannot be applied to zero-width parsers; value {r.Value} at position {r.Location.Position}.\", r.Location.Position);\n\n                result.Add(r.Value);\n\n                from = r.Remainder;\n                r = parser(r.Remainder);\n            }\n\n            if (!r.Backtrack && r.IsPartial(from))\n                return Result.CastEmpty<T, T[]>(r);\n\n            return Result.Value(result.ToArray(), input, from);\n        };\n    }\n\n    /// <summary>\n    /// Construct a parser that matches <paramref name=\"parser\"/> zero or more times, delimited by <paramref name=\"delimiter\"/>.\n    /// </summary>\n    /// <typeparam name=\"TKind\">The kind of the tokens being parsed.</typeparam>\n    /// <typeparam name=\"T\">The type of value being parsed.</typeparam>\n    /// <typeparam name=\"U\">The type of the resulting value.</typeparam>\n    /// <param name=\"parser\">The parser.</param>\n    /// <param name=\"delimiter\">The parser that matches the delimiters.</param>\n    /// <param name=\"end\">A parser to match a final trailing delimiter, if required. Specifying\n    /// this can improve error reporting for some lists.</param>\n    /// <returns>The resulting parser.</returns>\n    public static TokenListParser<TKind, T[]> ManyDelimitedBy<TKind, T, U>(\n        this TokenListParser<TKind, T> parser,\n        TokenListParser<TKind, U> delimiter,\n        TokenListParser<TKind, U>? end = null)\n    {\n        if (parser == null) throw new ArgumentNullException(nameof(parser));\n        if (delimiter == null) throw new ArgumentNullException(nameof(delimiter));\n\n        // ReSharper disable once ConvertClosureToMethodGroup\n\n        if (end != null)\n            return parser\n                .AtLeastOnceDelimitedBy(delimiter)\n                .Then(p => end.Value(p))\n                .Or(end.Value(Array.Empty<T>()));\n\n        return parser\n            .Then(first => delimiter.IgnoreThen(parser).Many().Select(rest => (T[])[first, ..rest]))\n            .OptionalOrDefault([]);\n    }\n\n    /// <summary>\n    /// Construct a parser that returns <paramref name=\"name\"/> as its \"expectation\" if <paramref name=\"parser\"/> fails.\n    /// </summary>\n    /// <typeparam name=\"TKind\">The kind of the tokens being parsed.</typeparam>\n    /// <typeparam name=\"T\">The type of value being parsed.</typeparam>\n    /// <param name=\"parser\">The parser.</param>\n    /// <param name=\"name\">The name given to <paramref name=\"parser\"/>.</param>\n    /// <returns>The resulting parser.</returns>\n    public static TokenListParser<TKind, T> Named<TKind, T>(this TokenListParser<TKind, T> parser, string name)\n    {\n        if (parser == null) throw new ArgumentNullException(nameof(parser));\n        if (name == null) throw new ArgumentNullException(nameof(name));\n\n        return input =>\n        {\n            var result = parser(input);\n            if (result.HasValue || result.IsPartial(input))\n                return result;\n\n            return TokenListParserResult.Empty<TKind, T>(result.Remainder, [name]);\n        };\n    }\n\n    /// <summary>\n    /// Construct a parser that returns <paramref name=\"name\"/> as its \"expectation\" if <paramref name=\"parser\"/> fails.\n    /// </summary>\n    /// <typeparam name=\"T\">The type of value being parsed.</typeparam>\n    /// <param name=\"parser\">The parser.</param>\n    /// <param name=\"name\">The name given to <paramref name=\"parser\"/>.</param>\n    /// <returns>The resulting parser.</returns>\n    public static TextParser<T> Named<T>(this TextParser<T> parser, string name)\n    {\n        if (parser == null) throw new ArgumentNullException(nameof(parser));\n        if (name == null) throw new ArgumentNullException(nameof(name));\n\n        return input =>\n        {\n            var result = parser(input);\n            if (result.HasValue || result.IsPartial(input))\n                return result;\n\n            return Result.Empty<T>(result.Remainder, [name]);\n        };\n    }\n\n    /// <summary>\n    /// Construct a parser that matches zero or one instance of <paramref name=\"parser\"/>, returning <paramref name=\"defaultValue\"/> when\n    /// no match is possible.\n    /// </summary>\n    /// <typeparam name=\"TKind\">The kind of the tokens being parsed.</typeparam>\n    /// <typeparam name=\"T\">The type of value being parsed.</typeparam>\n    /// <param name=\"parser\">The parser.</param>\n    /// <param name=\"defaultValue\">The default value</param>\n    /// <returns>The resulting parser.</returns>\n    public static TokenListParser<TKind, T> OptionalOrDefault<TKind, T>(this TokenListParser<TKind, T> parser, T defaultValue = default!)\n    {\n        if (parser == null) throw new ArgumentNullException(nameof(parser));\n\n        return parser.Or(Parse.Return<TKind, T>(defaultValue!));\n    }\n\n    /// <summary>\n    /// Construct a parser that matches zero or one instance of <paramref name=\"parser\"/>, returning <paramref name=\"defaultValue\"/> when\n    /// no match is possible.\n    /// </summary>\n    /// <typeparam name=\"T\">The type of value being parsed.</typeparam>\n    /// <param name=\"parser\">The parser.</param>\n    /// <param name=\"defaultValue\">The default value.</param>\n    /// <returns>The resulting parser.</returns>\n    public static TextParser<T> OptionalOrDefault<T>(this TextParser<T> parser, T defaultValue = default!)\n    {\n        if (parser == null) throw new ArgumentNullException(nameof(parser));\n\n        return parser.Or(Parse.Return(defaultValue!));\n    }\n\n    /// <summary>\n    /// Construct a parser that tries first the <paramref name=\"lhs\"/> parser, and if it fails, applies <paramref name=\"rhs\"/>.\n    /// </summary>\n    /// <typeparam name=\"TKind\">The kind of the tokens being parsed.</typeparam>\n    /// <typeparam name=\"T\">The type of value being parsed.</typeparam>\n    /// <param name=\"lhs\">The first parser to try.</param>\n    /// <param name=\"rhs\">The second parser to try.</param>\n    /// <returns>The resulting parser.</returns>\n    /// <remarks>Or will fail if the first item partially matches this. To modify this behavior use <see cref=\"Try{TKind,T}(TokenListParser{TKind,T})\"/>.</remarks>\n    public static TokenListParser<TKind, T> Or<TKind, T>(this TokenListParser<TKind, T> lhs, TokenListParser<TKind, T> rhs)\n    {\n        if (lhs == null) throw new ArgumentNullException(nameof(lhs));\n        if (rhs == null) throw new ArgumentNullException(nameof(rhs));\n\n        return input =>\n        {\n            var first = lhs(input);\n            if (first.HasValue || !first.Backtrack && first.IsPartial(input))\n                return first;\n\n            var second = rhs(input);\n            if (second.HasValue)\n                return second;\n\n            return TokenListParserResult.CombineEmpty(first, second);\n        };\n    }\n\n    /// <summary>\n    /// Construct a parser that tries first the <paramref name=\"lhs\"/> parser, and if it fails, applies <paramref name=\"rhs\"/>.\n    /// </summary>\n    /// <typeparam name=\"T\">The type of value being parsed.</typeparam>\n    /// <param name=\"lhs\">The first parser to try.</param>\n    /// <param name=\"rhs\">The second parser to try.</param>\n    /// <returns>The resulting parser.</returns>\n    /// <remarks>Or will fail if the first item partially matches this. To modify this behavior use <see cref=\"Try{T}(TextParser{T})\"/>.</remarks>\n    public static TextParser<T> Or<T>(this TextParser<T> lhs, TextParser<T> rhs)\n    {\n        if (lhs == null) throw new ArgumentNullException(nameof(lhs));\n        if (rhs == null) throw new ArgumentNullException(nameof(rhs));\n\n        return input =>\n        {\n            var first = lhs(input);\n            if (first.HasValue || !first.Backtrack && first.IsPartial(input))\n                return first;\n\n            var second = rhs(input);\n            if (second.HasValue)\n                return second;\n\n            return Result.CombineEmpty(first, second);\n        };\n    }\n\n    /// <summary>\n    /// Construct a parser that takes the result of <paramref name=\"parser\"/> and converts it value using <paramref name=\"selector\"/>.\n    /// </summary>\n    /// <typeparam name=\"TKind\">The kind of the tokens being parsed.</typeparam>\n    /// <typeparam name=\"T\">The type of value being parsed.</typeparam>\n    /// <typeparam name=\"U\">The type of the resulting value.</typeparam>\n    /// <param name=\"parser\">The parser.</param>\n    /// <param name=\"selector\">A mapping from the first result to the second.</param>\n    /// <returns>The resulting parser.</returns>\n    public static TokenListParser<TKind, U> Select<TKind, T, U>(this TokenListParser<TKind, T> parser, Func<T, U> selector)\n    {\n        if (parser == null) throw new ArgumentNullException(nameof(parser));\n        if (selector == null) throw new ArgumentNullException(nameof(selector));\n\n        return input =>\n        {\n            var rt = parser(input);\n            if (!rt.HasValue)\n                return TokenListParserResult.CastEmpty<TKind, T, U>(rt);\n\n            var u = selector(rt.Value);\n\n            return TokenListParserResult.Value(u, input, rt.Remainder);\n        };\n    }\n\n    /// <summary>\n    /// Construct a parser that takes the result of <paramref name=\"parser\"/> and converts it value using <paramref name=\"selector\"/>.\n    /// </summary>\n    /// <typeparam name=\"T\">The type of value being parsed.</typeparam>\n    /// <typeparam name=\"U\">The type of the resulting value.</typeparam>\n    /// <param name=\"parser\">The parser.</param>\n    /// <param name=\"selector\">A mapping from the first result to the second.</param>\n    /// <returns>The resulting parser.</returns>\n    public static TextParser<U> Select<T, U>(this TextParser<T> parser, Func<T, U> selector)\n    {\n        if (parser == null) throw new ArgumentNullException(nameof(parser));\n        if (selector == null) throw new ArgumentNullException(nameof(selector));\n\n        return input =>\n        {\n            var rt = parser(input);\n            if (!rt.HasValue)\n                return Result.CastEmpty<T, U>(rt);\n\n            var u = selector(rt.Value);\n\n            return Result.Value(u, input, rt.Remainder);\n        };\n    }\n\n    /// <summary>\n    /// Construct a parser that takes the result of <paramref name=\"parser\"/> and casts it to <typeparamref name=\"U\"/>.\n    /// </summary>\n    /// <typeparam name=\"TKind\">The kind of the tokens being parsed.</typeparam>\n    /// <typeparam name=\"T\">The type of value being parsed.</typeparam>\n    /// <typeparam name=\"U\">The type of the resulting value.</typeparam>\n    /// <param name=\"parser\">The parser.</param>\n    /// <returns>The resulting parser.</returns>\n    public static TokenListParser<TKind, U> Cast<TKind, T, U>(this TokenListParser<TKind, T> parser)\n        where T: U\n    {\n        if (parser == null) throw new ArgumentNullException(nameof(parser));\n\n        return parser.Select(rt => (U)rt);\n    }\n\n    /// <summary>\n    /// The LINQ query comprehension pattern.\n    /// </summary>\n    /// <typeparam name=\"TKind\">The kind of the tokens being parsed.</typeparam>\n    /// <typeparam name=\"T\">The type of value being parsed.</typeparam>\n    /// <typeparam name=\"U\">The type of the resulting value.</typeparam>\n    /// <typeparam name=\"V\"></typeparam>\n    /// <param name=\"parser\">The parser.</param>\n    /// <param name=\"selector\">A mapping from the first result to the second parser.</param>\n    /// <param name=\"projector\">Function mapping the results of the first two parsers onto the final result.</param>\n    /// <returns>The resulting parser.</returns>\n    public static TokenListParser<TKind, V> SelectMany<TKind, T, U, V>(\n        this TokenListParser<TKind, T> parser,\n        Func<T, TokenListParser<TKind, U>> selector,\n        Func<T, U, V> projector)\n    {\n        if (parser == null) throw new ArgumentNullException(nameof(parser));\n        if (selector == null) throw new ArgumentNullException(nameof(selector));\n        if (projector == null) throw new ArgumentNullException(nameof(projector));\n\n        return parser.Then(t => selector(t).Select(u => projector(t, u)));\n    }\n\n    /// <summary>\n    /// Construct a parser that applies <paramref name=\"first\"/>, provides the value to <paramref name=\"second\"/> and returns the result.\n    /// </summary>\n    /// <typeparam name=\"TKind\">The kind of the tokens being parsed.</typeparam>\n    /// <typeparam name=\"T\">The type of value being parsed.</typeparam>\n    /// <typeparam name=\"U\">The type of the resulting value.</typeparam>\n    /// <param name=\"first\">The first parser.</param>\n    /// <param name=\"second\">The second parser.</param>\n    /// <returns>The resulting parser.</returns>\n    public static TokenListParser<TKind, U> Then<TKind, T, U>(this TokenListParser<TKind, T> first, Func<T, TokenListParser<TKind, U>> second)\n    {\n        if (first == null) throw new ArgumentNullException(nameof(first));\n        if (second == null) throw new ArgumentNullException(nameof(second));\n\n        return input =>\n        {\n            var rt = first(input);\n            if (!rt.HasValue)\n                return TokenListParserResult.CastEmpty<TKind, T, U>(rt);\n\n            var ru = second(rt.Value)(rt.Remainder);\n            if (!ru.HasValue)\n                return ru;\n\n            return TokenListParserResult.Value(ru.Value, input, ru.Remainder);\n        };\n    }\n\n    /// <summary>\n    /// Construct a parser that applies <paramref name=\"first\"/>, provides the value to <paramref name=\"second\"/> and returns the result.\n    /// </summary>\n    /// <typeparam name=\"T\">The type of value being parsed.</typeparam>\n    /// <typeparam name=\"U\">The type of the resulting value.</typeparam>\n    /// <param name=\"first\">The first parser.</param>\n    /// <param name=\"second\">The second parser.</param>\n    /// <returns>The resulting parser.</returns>\n    public static TextParser<U> Then<T, U>(this TextParser<T> first, Func<T, TextParser<U>> second)\n    {\n        if (first == null) throw new ArgumentNullException(nameof(first));\n        if (second == null) throw new ArgumentNullException(nameof(second));\n\n        return input =>\n        {\n            var rt = first(input);\n            if (!rt.HasValue)\n                return Result.CastEmpty<T, U>(rt);\n\n            var ru = second(rt.Value)(rt.Remainder);\n            if (!ru.HasValue)\n                return ru;\n\n            return Result.Value(ru.Value, input, ru.Remainder);\n        };\n    }\n\n    /// <summary>\n    /// Construct a parser that tries one parser, and backtracks if unsuccessful so that no input\n    /// appears to have been consumed by subsequent checks against the result.\n    /// </summary>\n    /// <typeparam name=\"TKind\">The kind of the tokens being parsed.</typeparam>\n    /// <typeparam name=\"T\">The type of value being parsed.</typeparam>\n    /// <param name=\"parser\">The parser.</param>\n    /// <returns>The resulting parser.</returns>\n    public static TokenListParser<TKind, T> Try<TKind, T>(this TokenListParser<TKind, T> parser)\n    {\n        if (parser == null) throw new ArgumentNullException(nameof(parser));\n\n        return input =>\n        {\n            var rt = parser(input);\n            if (rt.HasValue)\n                return rt;\n\n            rt.Backtrack = true;\n            return rt;\n        };\n    }\n\n    /// <summary>\n    /// Construct a parser that tries one parser, and backtracks if unsuccessful so that no input\n    /// appears to have been consumed by subsequent checks against the result.\n    /// </summary>\n    /// <typeparam name=\"T\">The type of value being parsed.</typeparam>\n    /// <param name=\"parser\">The parser.</param>\n    /// <returns>The resulting parser.</returns>\n    public static TextParser<T> Try<T>(this TextParser<T> parser)\n    {\n        if (parser == null) throw new ArgumentNullException(nameof(parser));\n\n        return input =>\n        {\n            var rt = parser(input);\n            if (rt.HasValue)\n                return rt;\n\n            rt.Backtrack = true;\n            return rt;\n        };\n    }\n\n    /// <summary>\n    /// Construct a parser that applies the first, and returns <paramref name=\"value\"/>.\n    /// </summary>\n    /// <typeparam name=\"TKind\">The kind of the tokens being parsed.</typeparam>\n    /// <typeparam name=\"T\">The type of value being parsed.</typeparam>\n    /// <typeparam name=\"U\">The type of the resulting value.</typeparam>\n    /// <param name=\"parser\">The parser.</param>\n    /// <param name=\"value\">The value to return.</param>\n    /// <returns>The resulting parser.</returns>\n    public static TokenListParser<TKind, U> Value<TKind, T, U>(this TokenListParser<TKind, T> parser, U value)\n    {\n        if (parser == null) throw new ArgumentNullException(nameof(parser));\n\n        return parser.IgnoreThen(Parse.Return<TKind, U>(value));\n    }\n\n    /// <summary>\n    /// Construct a parser that applies the first, and returns <paramref name=\"value\"/>.\n    /// </summary>\n    /// <typeparam name=\"T\">The type of value being parsed.</typeparam>\n    /// <typeparam name=\"U\">The type of the resulting value.</typeparam>\n    /// <param name=\"parser\">The parser.</param>\n    /// <param name=\"value\">The value to return.</param>\n    /// <returns>The resulting parser.</returns>\n    public static TextParser<U> Value<T, U>(this TextParser<T> parser, U value)\n    {\n        if (parser == null) throw new ArgumentNullException(nameof(parser));\n\n        return parser.IgnoreThen(Parse.Return(value));\n    }\n\n    /// <summary>\n    /// Parse a sequence of operands connected by left-associative operators.\n    /// </summary>\n    /// <typeparam name=\"TKind\">The kind of the tokens being parsed.</typeparam>\n    /// <typeparam name=\"TResult\">The type of the leftmost operand and of the ultimate result.</typeparam>\n    /// <typeparam name=\"TOperator\">The type of the operator.</typeparam>\n    /// <typeparam name=\"TOperand\">The type of subsequent operands.</typeparam>\n    /// <param name=\"parser\">The parser for the leftmost operand.</param>\n    /// <param name=\"operator\">A parser matching operators.</param>\n    /// <param name=\"operand\">A parser matching operands.</param>\n    /// <param name=\"apply\">A function combining the operator, left operand, and right operand, into the result.</param>\n    /// <returns>The result of calling <paramref name=\"apply\"/> successively on pairs of operands.</returns>\n    public static TokenListParser<TKind, TResult> Chain<TKind, TResult, TOperator, TOperand>(\n        this TokenListParser<TKind, TResult> parser,\n        TokenListParser<TKind, TOperator> @operator,\n        TokenListParser<TKind, TOperand> operand,\n        Func<TOperator, TResult, TOperand, TResult> apply)\n    {\n        if (parser == null) throw new ArgumentNullException(nameof(parser));\n        if (@operator == null) throw new ArgumentNullException(nameof(@operator));\n        if (operand == null) throw new ArgumentNullException(nameof(operand));\n        if (apply == null) throw new ArgumentNullException(nameof(apply));\n\n        return input =>\n        {\n            var parseResult = parser(input);\n            if (!parseResult.HasValue )\n                return parseResult;\n\n            var result = parseResult.Value;\n            var operandRemainder = parseResult.Remainder;\n\n            var operatorResult = @operator(operandRemainder);\n            while (operatorResult.HasValue || operatorResult.IsPartial(operandRemainder))\n            {\n                // If operator read any input, but failed to read complete input, we return error\n                if (!operatorResult.HasValue)\n                    return TokenListParserResult.CastEmpty<TKind, TOperator, TResult>(operatorResult);\n\n                var operandResult = operand(operatorResult.Remainder);\n                operandRemainder = operandResult.Remainder;\n\n                if (!operandResult.HasValue)\n                    return TokenListParserResult.CastEmpty<TKind, TOperand, TResult>(operandResult);\n\n                result = apply(operatorResult.Value, result, operandResult.Value);\n\n                operatorResult = @operator(operandRemainder);\n            }\n\n            return TokenListParserResult.Value(result, input, operandRemainder);\n        };\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/ParserConstruction/Display/Presentation.cs",
    "content": "﻿// Copyright 2016 Datalust, Superpower Contributors, Sprache Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing System.Reflection;\nusing Serilog.ParserConstruction.Util;\n\nnamespace Serilog.ParserConstruction.Display;\n\nstatic class Presentation\n{\n    static string FormatKind(object kind)\n    {\n        return kind.ToString()!.ToLower();\n    }\n\n    static TokenAttribute? TryGetTokenAttribute(Type type)\n    {\n        return type.GetTypeInfo().GetCustomAttribute<TokenAttribute>();\n    }\n\n    static TokenAttribute? TryGetTokenAttribute<TKind>(TKind kind)\n    {\n        var kindTypeInfo = typeof(TKind).GetTypeInfo();\n        if (kindTypeInfo.IsEnum)\n        {\n            var field = kindTypeInfo.GetDeclaredField(kind!.ToString()!);\n            if (field != null)\n            {\n                return field.GetCustomAttribute<TokenAttribute>() ?? TryGetTokenAttribute(typeof(TKind));\n            }\n        }\n\n        return TryGetTokenAttribute(typeof(TKind));\n    }\n\n    public static string FormatExpectation<TKind>(TKind kind)\n    {\n        var description = TryGetTokenAttribute(kind);\n        if (description != null)\n        {\n            if (description.Description != null)\n                return description.Description;\n            if (description.Example != null)\n                return FormatLiteral(description.Example);\n        }\n\n        return FormatKind(kind!);\n    }\n\n    public static string FormatAppearance<TKind>(TKind kind, string value)\n    {\n        var clipped = FormatLiteral(Friendly.Clip(value, 12));\n\n        var description = TryGetTokenAttribute(kind);\n        if (description != null)\n        {\n            if (description.Category != null)\n                return $\"{description.Category} {clipped}\";\n\n            if (description.Example != null)\n                return clipped;\n        }\n\n        return $\"{FormatKind(kind!)} {clipped}\";\n    }\n    public static string FormatLiteral(char literal)\n    {\n        switch (literal)\n        {\n            //Unicode Category: Space Separators\n            case '\\x00A0': return \"U+00A0 no-break space\";\n            case '\\x1680': return \"U+1680 ogham space mark\";\n            case '\\x2000': return \"U+2000 en quad\";\n            case '\\x2001': return \"U+2001 em quad\";\n            case '\\x2002': return \"U+2002 en space\";\n            case '\\x2003': return \"U+2003 em space\";\n            case '\\x2004': return \"U+2004 three-per-em space\";\n            case '\\x2005': return \"U+2005 four-per-em space\";\n            case '\\x2006': return \"U+2006 six-per-em space\";\n            case '\\x2007': return \"U+2007 figure space\";\n            case '\\x2008': return \"U+2008 punctuation space\";\n            case '\\x2009': return \"U+2009 thin space\";\n            case '\\x200A': return \"U+200A hair space\";\n            case '\\x202F': return \"U+202F narrow no-break space\";\n            case '\\x205F': return \"U+205F medium mathematical space\";\n            case '\\x3000': return \"U+3000 ideographic space\";\n\n            //Line Separator\n            case '\\x2028': return \"U+2028 line separator\";\n\n            //Paragraph Separator\n            case '\\x2029': return \"U+2029 paragraph separator\";\n\n            //Unicode C0 Control Codes (ASCII equivalent)\n            case '\\x0000': return \"NUL\"; //\\0\n            case '\\x0001': return \"U+0001 start of heading\";\n            case '\\x0002': return \"U+0002 start of text\";\n            case '\\x0003': return \"U+0003 end of text\";\n            case '\\x0004': return \"U+0004 end of transmission\";\n            case '\\x0005': return \"U+0005 enquiry\";\n            case '\\x0006': return \"U+0006 acknowledge\";\n            case '\\x0007': return \"U+0007 bell\";\n            case '\\x0008': return \"U+0008 backspace\";\n            case '\\x0009': return \"tab\"; //\\t\n            case '\\x000A': return \"line feed\"; //\\n\n            case '\\x000B': return \"U+000B vertical tab\";\n            case '\\x000C': return \"U+000C form feed\";\n            case '\\x000D': return \"carriage return\"; //\\r\n            case '\\x000E': return \"U+000E shift in\";\n            case '\\x000F': return \"U+000F shift out\";\n            case '\\x0010': return \"U+0010 data link escape\";\n            case '\\x0011': return \"U+0011 device ctrl 1\";\n            case '\\x0012': return \"U+0012 device ctrl 2\";\n            case '\\x0013': return \"U+0013 device ctrl 3\";\n            case '\\x0014': return \"U+0014 device ctrl 4\";\n            case '\\x0015': return \"U+0015 not acknowledge\";\n            case '\\x0016': return \"U+0016 synchronous idle\";\n            case '\\x0017': return \"U+0017 end transmission block\";\n            case '\\x0018': return \"U+0018 cancel\";\n            case '\\x0019': return \"U+0019 end of medium\";\n            case '\\x0020': return \"space\";\n            case '\\x001A': return \"U+001A substitute\";\n            case '\\x001B': return \"U+001B escape\";\n            case '\\x001C': return \"U+001C file separator\";\n            case '\\x001D': return \"U+001D group separator\";\n            case '\\x001E': return \"U+001E record separator\";\n            case '\\x001F': return \"U+001F unit separator\";\n            case '\\x007F': return \"U+007F delete\";\n\n            default: return \"`\" + literal + \"`\";\n        }\n    }\n\n    public static string FormatLiteral(string literal)\n    {\n        return \"`\" + literal + \"`\";\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/ParserConstruction/Display/TokenAttribute.cs",
    "content": "﻿// Copyright 2016 Datalust, Superpower Contributors, Sprache Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n\n\n// ReSharper disable UnusedAutoPropertyAccessor.Global, ClassNeverInstantiated.Global\n\nnamespace Serilog.ParserConstruction.Display;\n\n/// <summary>\n/// Applied to enum members representing tokens to control how they are rendered.\n/// </summary>\n[AttributeUsage(AttributeTargets.Field|AttributeTargets.Class)]\nclass TokenAttribute : Attribute\n{\n    /// <summary>\n    /// The category of the token, e.g. \"keyword\" or \"identifier\".\n    /// </summary>\n    public string? Category { get; set; }\n\n    /// <summary>\n    /// For tokens that correspond to exact text, e.g. punctuation, the canonical\n    /// example of how the token looks.\n    /// </summary>\n    public string? Example { get; set; }\n\n    /// <summary>\n    /// A description of the token, for example \"regular expression\".\n    /// </summary>\n    public string? Description { get; set; }\n}"
  },
  {
    "path": "src/Serilog.Expressions/ParserConstruction/Model/Position.cs",
    "content": "// Copyright 2016 Datalust, Superpower Contributors, Sprache Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nnamespace Serilog.ParserConstruction.Model;\n\n/// <summary>\n/// A position within a stream of character input.\n/// </summary>\nreadonly struct Position\n{\n    /// <summary>\n    /// The zero-based absolute index of the position.\n    /// </summary>\n    public int Absolute { get; }\n\n    /// <summary>\n    /// The one-based line number.\n    /// </summary>\n    public int Line { get; }\n\n    /// <summary>\n    /// The one-based column number.\n    /// </summary>\n    public int Column { get; }\n\n    /// <summary>\n    /// Construct a position.\n    /// </summary>\n    /// <param name=\"absolute\">The absolute position.</param>\n    /// <param name=\"line\">The line number.</param>\n    /// <param name=\"column\">The column number.</param>\n    Position(int absolute, int line, int column)\n    {\n#if CHECKED\n            if (absolute < 0) throw new ArgumentOutOfRangeException(nameof(line), \"Absolute positions start at 0.\");\n            if (line < 1) throw new ArgumentOutOfRangeException(nameof(line), \"Line numbering starts at 1.\");\n            if (column < 1) throw new ArgumentOutOfRangeException(nameof(column), \"Column numbering starts at 1.\");\n#endif\n        Absolute = absolute;\n        Line = line;\n        Column = column;\n    }\n\n    /// <summary>\n    /// The position corresponding to the zero index.\n    /// </summary>\n    public static Position Zero { get; } = new(0, 1, 1);\n\n    /// <summary>\n    /// A position with no value.\n    /// </summary>\n    public static Position Empty => default;\n\n    /// <summary>\n    /// True if the position has a value.\n    /// </summary>\n    public bool HasValue => Line > 0;\n\n    /// <summary>\n    /// Advance over <paramref name=\"overChar\"/>, advancing line and column numbers\n    /// as appropriate.\n    /// </summary>\n    /// <param name=\"overChar\">The character being advanced over.</param>\n    /// <returns>The updated position.</returns>\n    public Position Advance(char overChar)\n    {\n        if (overChar == '\\n')\n            return new(Absolute + 1, Line + 1, 1);\n\n        return new(Absolute + 1, Line, Column + 1);\n    }\n\n    /// <inheritdoc/>\n    public override string ToString()\n    {\n        return $\"{Absolute} (line {Line}, column {Column})\";\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/ParserConstruction/Model/Result.cs",
    "content": "// Copyright 2016 Datalust, Superpower Contributors, Sprache Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.ParserConstruction.Util;\n\nnamespace Serilog.ParserConstruction.Model;\n\n/// <summary>\n/// Helper methods for working with <see cref=\"Result{T}\"/>.\n/// </summary>\nstatic class Result\n{\n    /// <summary>\n    /// An empty result indicating no value could be parsed.\n    /// </summary>\n    /// <typeparam name=\"T\">The result type.</typeparam>\n    /// <param name=\"remainder\">The start of un-parsed input.</param>\n    /// <returns>A result.</returns>\n    public static Result<T> Empty<T>(TextSpan remainder)\n    {\n        return new(remainder, null, null, false);\n    }\n\n    /// <summary>\n    /// An empty result indicating no value could be parsed.\n    /// </summary>\n    /// <typeparam name=\"T\">The result type.</typeparam>\n    /// <param name=\"remainder\">The start of un-parsed input.</param>\n    /// <param name=\"expectations\">Literal descriptions of expectations not met.</param>\n    /// <returns>A result.</returns>\n    public static Result<T> Empty<T>(TextSpan remainder, string[] expectations)\n    {\n        return new(remainder, null, expectations, false);\n    }\n\n    /// <summary>\n    /// A result carrying a successfully-parsed value.\n    /// </summary>\n    /// <typeparam name=\"T\">The result type.</typeparam>\n    /// <param name=\"value\">The value.</param>\n    /// <param name=\"location\">The location corresponding to the beginning of the parsed span.</param>\n    /// <param name=\"remainder\">The start of un-parsed input.</param>\n    /// <returns>A result.</returns>\n    public static Result<T> Value<T>(T value, TextSpan location, TextSpan remainder)\n    {\n        return new(value, location, remainder, false);\n    }\n\n    /// <summary>\n    /// Convert an empty result of one type into another.\n    /// </summary>\n    /// <typeparam name=\"T\">The source type.</typeparam>\n    /// <typeparam name=\"U\">The target type.</typeparam>\n    /// <param name=\"result\">The value to convert.</param>\n    /// <returns>A result of type <typeparamref name=\"U\"/> carrying the same information as <paramref name=\"result\"/>.</returns>\n    public static Result<U> CastEmpty<T, U>(Result<T> result)\n    {\n        return new(result.Remainder, result.ErrorMessage, result.Expectations, result.Backtrack);\n    }\n\n    /// <summary>\n    /// Combine two empty results.\n    /// </summary>\n    /// <typeparam name=\"T\">The source type.</typeparam>\n    /// <param name=\"first\">The first value to combine.</param>\n    /// <param name=\"second\">The second value to combine.</param>\n    /// <returns>A result of type <typeparamref name=\"T\"/> carrying information from both results.</returns>\n    public static Result<T> CombineEmpty<T>(Result<T> first, Result<T> second)\n    {\n        if (first.Remainder != second.Remainder)\n            return second;\n\n        var expectations = first.Expectations;\n        if (expectations == null)\n            expectations = second.Expectations;\n        else if (second.Expectations != null)\n            expectations = [..first.Expectations!, ..second.Expectations];\n\n        return new(second.Remainder, second.ErrorMessage, expectations, second.Backtrack);\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/ParserConstruction/Model/Result`1.cs",
    "content": "﻿// Copyright 2016 Datalust, Superpower Contributors, Sprache Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.ParserConstruction.Util;\n\nnamespace Serilog.ParserConstruction.Model;\n\n/// <summary>\n/// The result of parsing from a text span.\n/// </summary>\n/// <typeparam name=\"T\">The type of the value being parsed.</typeparam>\nstruct Result<T>\n{\n    readonly T _value;\n\n    /// <summary>\n    /// If the result is a value, the location in the input corresponding to the\n    /// value. If the result is an error, it's the location of the error.\n    /// </summary>\n    public TextSpan Location { get; }\n\n    /// <summary>\n    /// The first un-parsed location in the input.\n    /// </summary>\n    public TextSpan Remainder { get; }\n\n    /// <summary>\n    /// True if the result carries a successfully-parsed value; otherwise, false.\n    /// </summary>\n    public bool HasValue { get; }\n\n    /// <summary>\n    /// If the result is an error, the source-level position of the error; otherwise, <see cref=\"Position.Empty\"/>.\n    /// </summary>\n    public Position ErrorPosition => HasValue ? Position.Empty : Location.Position;\n\n    /// <summary>\n    /// A provided error message, or null.\n    /// </summary>\n    public string? ErrorMessage { get; }\n\n    /// <summary>\n    /// A list of expectations that were unmet, or null.\n    /// </summary>\n    public string[]? Expectations { get; }\n\n    internal bool IsPartial(TextSpan from) => from != Remainder;\n\n    internal bool Backtrack { get; set; }\n\n    /// <summary>\n    /// The parsed value.\n    /// </summary>\n    public T Value\n    {\n        get\n        {\n            if (!HasValue)\n                throw new InvalidOperationException($\"{nameof(Result)} has no value.\");\n            return _value;\n        }\n    }\n\n    internal Result(T value, TextSpan location, TextSpan remainder, bool backtrack)\n    {\n        Location = location;\n        Remainder = remainder;\n        _value = value;\n        HasValue = true;\n        ErrorMessage = null;\n        Expectations = null;\n        Backtrack = backtrack;\n    }\n\n    internal Result(TextSpan remainder, string? errorMessage, string[]? expectations, bool backtrack)\n    {\n        Location = Remainder = remainder;\n        _value = default!; // Default value is not observable.\n        HasValue = false;\n        Expectations = expectations;\n        ErrorMessage = errorMessage;\n        Backtrack = backtrack;\n    }\n\n    /// <inheritdoc />\n    public override string ToString()\n    {\n        if (Remainder == TextSpan.None)\n            return \"(Empty result.)\";\n\n        if (HasValue)\n            return $\"Successful parsing of {Value}.\";\n\n        var message = FormatErrorMessageFragment();\n        var location = \"\";\n        if (!Location.IsAtEnd)\n        {\n            location = $\" (line {Location.Position.Line}, column {Location.Position.Column})\";\n        }\n\n        return $\"Syntax error{location}: {message}.\";\n    }\n\n    /// <summary>\n    /// If the result is empty, format the fragment of text describing the error.\n    /// </summary>\n    /// <returns>The error fragment.</returns>\n    public string FormatErrorMessageFragment()\n    {\n        if (ErrorMessage != null)\n            return ErrorMessage;\n\n        string message;\n        if (Location.IsAtEnd)\n        {\n            message = \"unexpected end of input\";\n        }\n        else\n        {\n            var next = Location.ConsumeChar().Value;\n            message = $\"unexpected {Display.Presentation.FormatLiteral(next)}\";\n        }\n\n        if (Expectations != null)\n        {\n            var expected = Friendly.List(Expectations);\n            message += $\", expected {expected}\";\n        }\n\n        return message;\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/ParserConstruction/Model/TextSpan.cs",
    "content": "// Copyright 2016 Datalust, Superpower Contributors, Sprache Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nnamespace Serilog.ParserConstruction.Model;\n\n/// <summary>\n/// A span of text within a larger string.\n/// </summary>\nreadonly struct TextSpan : IEquatable<TextSpan>\n{\n    /// <summary>\n    /// The source string containing the span.\n    /// </summary>\n    public string? Source { get; }\n\n    /// <summary>\n    /// The position of the start of the span within the string.\n    /// </summary>\n    public Position Position { get; }\n\n    /// <summary>\n    /// The length of the span.\n    /// </summary>\n    public int Length { get; }\n\n    /// <summary>\n    /// Construct a span encompassing an entire string.\n    /// </summary>\n    /// <param name=\"source\">The source string.</param>\n    public TextSpan(string source)\n        : this(source, Position.Zero, source.Length)\n    {\n    }\n\n    /// <summary>\n    /// Construct a string span for a substring of <paramref name=\"source\"/>.\n    /// </summary>\n    /// <param name=\"source\">The source string.</param>\n    /// <param name=\"position\">The start of the span.</param>\n    /// <param name=\"length\">The length of the span.</param>\n    public TextSpan(string source, Position position, int length)\n    {\n#if CHECKED\n            if (source == null) throw new ArgumentNullException(nameof(source));\n            if (length < 0)\n                throw new ArgumentOutOfRangeException(nameof(length), \"The length must be non-negative.\");\n            if (source.Length < position.Absolute + length)\n                throw new ArgumentOutOfRangeException(nameof(length), \"The token extends beyond the end of the input.\");\n#endif\n\n        Source = source;\n        Position = position;\n        Length = length;\n    }\n\n    /// <summary>\n    /// A span with no value.\n    /// </summary>\n    public static TextSpan None => default;\n\n    /// <summary>\n    /// True if the span has no content.\n    /// </summary>\n    public bool IsAtEnd\n    {\n        get\n        {\n            EnsureHasValue();\n            return Length == 0;\n        }\n    }\n\n    void EnsureHasValue()\n    {\n        if (Source == null)\n            throw new InvalidOperationException(\"String span has no value.\");\n    }\n\n    /// <summary>\n    /// Consume a character from the start of the span.\n    /// </summary>\n    /// <returns>A result with the character and remainder.</returns>\n    public Result<char> ConsumeChar()\n    {\n        EnsureHasValue();\n\n        if (IsAtEnd)\n            return Result.Empty<char>(this);\n\n        var ch = Source![Position.Absolute];\n        return Result.Value(ch, this, new(Source, Position.Advance(ch), Length - 1));\n    }\n\n    /// <inheritdoc/>\n    public override bool Equals(object? obj)\n    {\n        if (obj is not TextSpan other)\n            return false;\n\n        return Equals(other);\n    }\n\n    /// <inheritdoc/>\n    public override int GetHashCode()\n    {\n        unchecked\n        {\n            return ((Source?.GetHashCode() ?? 0) * 397) ^ Position.Absolute;\n        }\n    }\n\n    /// <summary>\n    /// Compare a string span with another using source identity\n    /// semantics - same source, same position, same length.\n    /// </summary>\n    /// <param name=\"other\">The other span.</param>\n    /// <returns>True if the spans are the same.</returns>\n    public bool Equals(TextSpan other)\n    {\n        return ReferenceEquals(Source, other.Source) &&\n               Position.Absolute == other.Position.Absolute &&\n               Length == other.Length;\n    }\n\n    /// <summary>\n    /// Compare two spans using source identity semantics.\n    /// </summary>\n    /// <param name=\"lhs\">One span.</param>\n    /// <param name=\"rhs\">Another span.</param>\n    /// <returns>True if the spans are the same.</returns>\n    public static bool operator ==(TextSpan lhs, TextSpan rhs)\n    {\n        return lhs.Equals(rhs);\n    }\n\n    /// <summary>\n    /// Compare two spans using source identity semantics.\n    /// </summary>\n    /// <param name=\"lhs\">One span.</param>\n    /// <param name=\"rhs\">Another span.</param>\n    /// <returns>True if the spans are the different.</returns>\n    public static bool operator !=(TextSpan lhs, TextSpan rhs)\n    {\n        return !(lhs == rhs);\n    }\n\n    /// <summary>\n    /// Return a new span from the start of this span to the beginning of another.\n    /// </summary>\n    /// <param name=\"next\">The next span.</param>\n    /// <returns>A sub-span.</returns>\n    public TextSpan Until(TextSpan next)\n    {\n#if CHECKED\n            next.EnsureHasValue();\n            if (next.Source != Source) throw new ArgumentException(\"The spans are on different source strings.\", nameof(next));\n#endif\n        var charCount = next.Position.Absolute - Position.Absolute;\n        return First(charCount);\n    }\n\n    /// <summary>\n    /// Return a span comprising the first <paramref name=\"length\"/> characters of this span.\n    /// </summary>\n    /// <param name=\"length\">The number of characters to return.</param>\n    /// <returns>The sub-span.</returns>\n    TextSpan First(int length)\n    {\n#if CHECKED\n            if (length > Length)\n                throw new ArgumentOutOfRangeException(nameof(length), \"Length exceeds the source span's length.\");\n#endif\n\n        return new(Source!, Position, length);\n    }\n\n    /// <inheritdoc/>\n    public override string ToString()\n    {\n        if (Source == null)\n            return \"(empty source span)\";\n\n        return ToStringValue();\n    }\n\n    /// <summary>\n    /// Compute the string value of this span.\n    /// </summary>\n    /// <returns>A string with the value of this span.</returns>\n    public string ToStringValue()\n    {\n        EnsureHasValue();\n        return Source!.Substring(Position.Absolute, Length);\n    }\n\n    /// <summary>\n    /// Compare the contents of this span with <paramref name=\"otherValue\"/>, ignoring invariant character case.\n    /// </summary>\n    /// <param name=\"otherValue\">The string value to compare.</param>\n    /// <returns>True if the values are the same ignoring case.</returns>\n    public bool EqualsValueIgnoreCase(string otherValue)\n    {\n        if (otherValue == null) throw new ArgumentNullException(nameof(otherValue));\n        EnsureHasValue();\n        if (Length != otherValue.Length)\n            return false;\n        for (var i = 0; i < Length; ++i)\n        {\n            if (char.ToUpperInvariant(Source![Position.Absolute + i]) != char.ToUpperInvariant(otherValue[i]))\n                return false;\n        }\n        return true;\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/ParserConstruction/Model/TokenListParserResult.cs",
    "content": "// Copyright 2016 Datalust, Superpower Contributors, Sprache Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nnamespace Serilog.ParserConstruction.Model;\n\n/// <summary>\n/// Helper methods for working with <see cref=\"TokenListParserResult{TKind,T}\"/>.\n/// </summary>\nstatic class TokenListParserResult\n{\n    /// <summary>\n    /// Create a token result with no value, indicating a failure to parse any value.\n    /// </summary>\n    /// <typeparam name=\"TKind\">The kind of token.</typeparam>\n    /// <typeparam name=\"T\">The result type.</typeparam>\n    /// <param name=\"remainder\">The start of un-parsed input.</param>\n    /// <returns>An empty result.</returns>\n    public static TokenListParserResult<TKind, T> Empty<TKind, T>(TokenList<TKind> remainder)\n    {\n        return new(remainder, Position.Empty, null, null, false);\n    }\n\n    /// <summary>\n    /// Create a token result with no value, indicating a failure to parse any value.\n    /// </summary>\n    /// <typeparam name=\"TKind\">The kind of token.</typeparam>\n    /// <typeparam name=\"T\">The result type.</typeparam>\n    /// <param name=\"remainder\">The start of un-parsed input.</param>\n    /// <param name=\"expectations\">Expectations that could not be fulfilled.</param>\n    /// <returns>An empty result.</returns>\n    public static TokenListParserResult<TKind, T> Empty<TKind, T>(TokenList<TKind> remainder, string[] expectations)\n    {\n        return new(remainder, Position.Empty, null, expectations, false);\n    }\n\n    /// <summary>\n    /// Create a token result with no value, indicating a failure to parse any value.\n    /// </summary>\n    /// <typeparam name=\"TKind\">The kind of token.</typeparam>\n    /// <typeparam name=\"T\">The result type.</typeparam>\n    /// <param name=\"remainder\">The start of un-parsed input.</param>\n    /// <param name=\"errorMessage\">An error message describing why the tokens could not be parsed.</param>\n    /// <returns>An empty result.</returns>\n    public static TokenListParserResult<TKind, T> Empty<TKind, T>(TokenList<TKind> remainder, string errorMessage)\n    {\n        return new(remainder, Position.Empty, errorMessage, null, false);\n    }\n\n    /// <summary>\n    /// Create a token result with the provided value.\n    /// </summary>\n    /// <typeparam name=\"TKind\">The kind of token.</typeparam>\n    /// <typeparam name=\"T\">The result type.</typeparam>\n    /// <param name=\"value\">The value.</param>\n    /// <param name=\"location\">The location where parsing began.</param>\n    /// <param name=\"remainder\">The first un-parsed location.</param>\n    /// <returns></returns>\n    public static TokenListParserResult<TKind, T> Value<TKind, T>(T value, TokenList<TKind> location, TokenList<TKind> remainder)\n    {\n        return new(value, location, remainder, false);\n    }\n\n    /// <summary>\n    /// Convert an empty result of one type into another.\n    /// </summary>\n    /// <typeparam name=\"TKind\">The kind of token.</typeparam>\n    /// <typeparam name=\"T\">The source type.</typeparam>\n    /// <typeparam name=\"U\">The destination type.</typeparam>\n    /// <param name=\"result\">The result to convert.</param>\n    /// <returns>The converted result.</returns>\n    public static TokenListParserResult<TKind,U> CastEmpty<TKind, T, U>(TokenListParserResult<TKind, T> result)\n    {\n        return new(result.Remainder, result.SubTokenErrorPosition, result.ErrorMessage, result.Expectations, result.Backtrack);\n    }\n\n    /// <summary>\n    /// Combine two empty results.\n    /// </summary>\n    /// <typeparam name=\"T\">The source type.</typeparam>\n    /// <typeparam name=\"TKind\">The kind of token.</typeparam>\n    /// <param name=\"first\">The first value to combine.</param>\n    /// <param name=\"second\">The second value to combine.</param>\n    /// <returns>A result of type <typeparamref name=\"T\"/> carrying information from both results.</returns>\n    public static TokenListParserResult<TKind, T> CombineEmpty<TKind, T>(TokenListParserResult<TKind, T> first, TokenListParserResult<TKind, T> second)\n    {\n        if (first.Remainder != second.Remainder)\n            return second;\n\n        var expectations = first.Expectations;\n        if (expectations == null)\n            expectations = second.Expectations;\n        else if (second.Expectations != null)\n        {\n            expectations = new string[first.Expectations!.Length + second.Expectations.Length];\n            var i = 0;\n            for (; i < first.Expectations!.Length; ++i)\n                expectations[i] = first.Expectations![i];\n            for (var j = 0; j < second.Expectations.Length; ++i, ++j)\n                expectations[i] = second.Expectations[j];\n        }\n\n        return new(second.Remainder, second.SubTokenErrorPosition, first.ErrorMessage, expectations, second.Backtrack);\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/ParserConstruction/Model/TokenListParserResult`2.cs",
    "content": "﻿// Copyright 2016 Datalust, Superpower Contributors, Sprache Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.ParserConstruction.Display;\nusing Serilog.ParserConstruction.Util;\n\nnamespace Serilog.ParserConstruction.Model;\n\n/// <summary>\n/// The result of parsing from a token list.\n/// </summary>\n/// <typeparam name=\"T\">The type of the value being parsed.</typeparam>\n/// <typeparam name=\"TKind\">The kind of token being parsed.</typeparam>\nstruct TokenListParserResult<TKind, T>\n{\n    readonly T _value;\n\n    /// <summary>\n    /// If the result has a value, this carries the location of the value in the token\n    /// list. If the result is an error, it's the location of the error.\n    /// </summary>\n    public TokenList<TKind> Location { get; }\n\n    /// <summary>\n    /// The first un-parsed location in the list.\n    /// </summary>\n    public TokenList<TKind> Remainder { get; }\n\n    /// <summary>\n    /// True if the result carries a successfully-parsed value; otherwise, false.\n    /// </summary>\n    public bool HasValue { get; }\n\n    /// <summary>\n    /// If the result is an error, the source-level position of the error; otherwise, <see cref=\"Position.Empty\"/>.\n    /// </summary>\n    public Position ErrorPosition\n    {\n        get\n        {\n            if (HasValue)\n                return Position.Empty;\n\n            if (SubTokenErrorPosition.HasValue)\n                return SubTokenErrorPosition;\n\n            if (!Remainder.IsAtEnd)\n                return Remainder.ConsumeToken().Value.Position;\n\n            return Location.ComputeEndOfInputPosition();\n        }\n    }\n\n    /// <summary>\n    /// If the result is an error, the source-level position of the error; otherwise, <see cref=\"Position.Empty\"/>.\n    /// </summary>\n    public Position SubTokenErrorPosition { get; }\n\n    /// <summary>\n    /// A provided error message, or null.\n    /// </summary>\n    public string? ErrorMessage { get; }\n\n    /// <summary>\n    /// A list of expectations that were unmet, or null.\n    /// </summary>\n    public string[]? Expectations { get; }\n\n    /// <summary>\n    /// The parsed value.\n    /// </summary>\n    public T Value\n    {\n        get\n        {\n            if (!HasValue)\n                throw new InvalidOperationException($\"{nameof(TokenListParserResult)} has no value.\");\n            return _value;\n        }\n    }\n\n    internal bool IsPartial(TokenList<TKind> from) => SubTokenErrorPosition.HasValue || from != Remainder;\n\n    internal bool Backtrack { get; set; }\n\n    internal TokenListParserResult(T value, TokenList<TKind> location, TokenList<TKind> remainder, bool backtrack)\n    {\n        Location = location;\n        Remainder = remainder;\n        _value = value;\n        HasValue = true;\n        SubTokenErrorPosition = Position.Empty;\n        ErrorMessage = null;\n        Expectations = null;\n        Backtrack = backtrack;\n    }\n\n    internal TokenListParserResult(TokenList<TKind> location, TokenList<TKind> remainder, Position errorPosition, string? errorMessage, string[]? expectations, bool backtrack)\n    {\n        Location = location;\n        Remainder = remainder;\n        _value = default!; // Default value is not observable.\n        HasValue = false;\n        SubTokenErrorPosition = errorPosition;\n        ErrorMessage = errorMessage;\n        Expectations = expectations;\n        Backtrack = backtrack;\n    }\n\n    internal TokenListParserResult(TokenList<TKind> remainder, Position errorPosition, string? errorMessage, string[]? expectations, bool backtrack)\n    {\n        Location = Remainder = remainder;\n        _value = default!; // Default value is not observable.\n        HasValue = false;\n        SubTokenErrorPosition = errorPosition;\n        ErrorMessage = errorMessage;\n        Expectations = expectations;\n        Backtrack = backtrack;\n    }\n\n    /// <inheritdoc />\n    public override string ToString()\n    {\n        if (Remainder == TokenList<TKind>.Empty)\n            return \"(Empty result.)\";\n\n        if (HasValue)\n            return $\"Successful parsing of {Value}.\";\n\n        var message = FormatErrorMessageFragment();\n        var location = \"\";\n        if (!Remainder.IsAtEnd)\n        {\n            // Since the message notes `end of input`, don't report line/column here.\n            var sourcePosition = SubTokenErrorPosition.HasValue ? SubTokenErrorPosition : Remainder.ConsumeToken().Value.Position;\n            location = $\" (line {sourcePosition.Line}, column {sourcePosition.Column})\";\n        }\n\n        return $\"Syntax error{location}: {message}.\";\n    }\n\n    /// <summary>\n    /// If the result is empty, format the fragment of text describing the error.\n    /// </summary>\n    /// <returns>The error fragment.</returns>\n    string FormatErrorMessageFragment()\n    {\n        if (ErrorMessage != null)\n            return ErrorMessage;\n\n        string message;\n        if (Remainder.IsAtEnd)\n        {\n            message = \"unexpected end of input\";\n        }\n        else\n        {\n            var next = Remainder.ConsumeToken().Value;\n            var appearance = Presentation.FormatAppearance(next.Kind, next.ToStringValue());\n            message = $\"unexpected {appearance}\";\n        }\n\n        if (Expectations != null)\n        {\n            var expected = Friendly.List(Expectations);\n            message += $\", expected {expected}\";\n        }\n\n        return message;\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/ParserConstruction/Model/TokenList`1.cs",
    "content": "// Copyright 2016 Datalust, Superpower Contributors, Sprache Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing System.Collections;\n\nnamespace Serilog.ParserConstruction.Model;\n\n/// <summary>\n/// A list of <see cref=\"Token{TKind}\"/>\n/// </summary>\n/// <typeparam name=\"TKind\">The kind of tokens held in the list.</typeparam>\nreadonly struct TokenList<TKind> : IEquatable<TokenList<TKind>>, IEnumerable<Token<TKind>>\n{\n    readonly Token<TKind>[]? _tokens;\n\n    /// <summary>\n    /// The position of the token list in the token stream.\n    /// </summary>\n    public int Position { get; }\n\n    /// <summary>\n    /// Construct a token list containing <paramref name=\"tokens\"/>.\n    /// </summary>\n    /// <param name=\"tokens\">The tokens in the list.</param>\n    public TokenList(Token<TKind>[] tokens)\n        : this(tokens, 0)\n    {\n        if (tokens == null) throw new ArgumentNullException(nameof(tokens));\n    }\n\n    TokenList(Token<TKind>[] tokens, int position)\n    {\n#if CHECKED // Called on every advance or backtrack\n            if (tokens == null) throw new ArgumentNullException(nameof(tokens));\n            if (position > tokens.Length) throw new ArgumentOutOfRangeException(nameof(position), \"Position is past end + 1.\");\n#endif\n\n        Position = position;\n        _tokens = tokens;\n    }\n\n    /// <summary>\n    /// A token list with no value.\n    /// </summary>\n    public static TokenList<TKind> Empty { get; } = default;\n\n    /// <summary>\n    /// True if the token list contains no tokens.\n    /// </summary>\n    public bool IsAtEnd\n    {\n        get\n        {\n            EnsureHasValue();\n            return Position == _tokens!.Length;\n        }\n    }\n\n    void EnsureHasValue()\n    {\n        if (_tokens == null)\n            throw new InvalidOperationException(\"Token list has no value.\");\n    }\n\n    /// <summary>\n    /// Consume a token from the start of the list, returning a result with the token and remainder.\n    /// </summary>\n    /// <returns></returns>\n    public TokenListParserResult<TKind, Token<TKind>> ConsumeToken()\n    {\n        EnsureHasValue();\n\n        if (IsAtEnd)\n            return TokenListParserResult.Empty<TKind, Token<TKind>>(this);\n\n        var token = _tokens![Position];\n        return TokenListParserResult.Value(token, this, new(_tokens, Position + 1));\n    }\n\n    /// <inheritdoc/>\n    public IEnumerator<Token<TKind>> GetEnumerator()\n    {\n        EnsureHasValue();\n\n        for (var position = Position; position < _tokens!.Length; ++position)\n            yield return _tokens[position];\n    }\n\n    IEnumerator IEnumerable.GetEnumerator()\n    {\n        return GetEnumerator();\n    }\n\n    /// <inheritdoc/>\n    public override bool Equals(object? obj)\n    {\n        if (obj is not TokenList<TKind> other)\n            return false;\n\n        return Equals(other);\n    }\n\n    /// <inheritdoc/>\n    public override int GetHashCode()\n    {\n        unchecked\n        {\n            return ((_tokens?.GetHashCode() ?? 0) * 397) ^ Position;\n        }\n    }\n\n    /// <summary>\n    /// Compare two token lists using identity semantics - same list, same position.\n    /// </summary>\n    /// <param name=\"other\">The other token list.</param>\n    /// <returns>True if the token lists are the same.</returns>\n    public bool Equals(TokenList<TKind> other)\n    {\n        return Equals(_tokens, other._tokens) && Position == other.Position;\n    }\n\n    /// <summary>\n    /// Compare two token lists using identity semantics.\n    /// </summary>\n    /// <param name=\"lhs\">The first token list.</param>\n    /// <param name=\"rhs\">The second token list.</param>\n    /// <returns>True if the token lists are the same.</returns>\n    public static bool operator ==(TokenList<TKind> lhs, TokenList<TKind> rhs)\n    {\n        return lhs.Equals(rhs);\n    }\n\n    /// <summary>\n    /// Compare two token lists using identity semantics.\n    /// </summary>\n    /// <param name=\"lhs\">The first token list.</param>\n    /// <param name=\"rhs\">The second token list.</param>\n    /// <returns>True if the token lists are the different.</returns>\n    public static bool operator !=(TokenList<TKind> lhs, TokenList<TKind> rhs)\n    {\n        return !(lhs == rhs);\n    }\n\n    /// <inheritdoc/>\n    public override string ToString()\n    {\n        if (_tokens == null)\n            return \"Token list (empty)\";\n\n        return \"Token list\";\n    }\n\n    // A mildly expensive way to find the \"end of input\" position for error reporting.\n    internal Position ComputeEndOfInputPosition()\n    {\n        EnsureHasValue();\n\n        if (_tokens!.Length == 0)\n            return Model.Position.Zero;\n\n        var lastSpan = _tokens[_tokens.Length - 1].Span;\n        var source = lastSpan.Source;\n        var position = lastSpan.Position;\n        for (var i = position.Absolute; i < source!.Length; ++i)\n            position = position.Advance(source[i]);\n        return position;\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/ParserConstruction/Model/Token`1.cs",
    "content": "// Copyright 2016 Datalust, Superpower Contributors, Sprache Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nnamespace Serilog.ParserConstruction.Model;\n\n/// <summary>\n/// A token.\n/// </summary>\n/// <typeparam name=\"TKind\">The type of the token's kind.</typeparam>\nreadonly struct Token<TKind>\n{\n    /// <summary>\n    /// The kind of the token.\n    /// </summary>\n    public TKind Kind { get; }\n\n    /// <summary>\n    /// The string span containing the value of the token.\n    /// </summary>\n    public TextSpan Span { get; }\n\n    /// <summary>\n    /// Get the string value of the token.\n    /// </summary>\n    /// <returns>The token as a string.</returns>\n    public string ToStringValue() => Span.ToStringValue();\n\n    /// <summary>\n    /// The position of the token within the source string.\n    /// </summary>\n    public Position Position => Span.Position;\n\n    /// <summary>\n    /// True if the token has a value.\n    /// </summary>\n    bool HasValue => Span != TextSpan.None;\n\n    /// <summary>\n    /// Construct a token.\n    /// </summary>\n    /// <param name=\"kind\">The kind of the token.</param>\n    /// <param name=\"span\">The span holding the token's value.</param>\n    public Token(TKind kind, TextSpan span)\n    {\n        Kind = kind;\n        Span = span;\n    }\n\n    /// <inheritdoc/>\n    public override string ToString()\n    {\n        if (!HasValue)\n            return \"(empty token)\";\n\n        return $\"{Kind}@{Position}: {Span}\";\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/ParserConstruction/Model/Unit.cs",
    "content": "﻿// Copyright 2016 Datalust, Superpower Contributors, Sprache Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nnamespace Serilog.ParserConstruction.Model;\n\n/// <summary>\n/// A structure with no information.\n/// </summary>\nstruct Unit\n{\n    /// <summary>\n    /// The singleton value of the struct, with no value.\n    /// </summary>\n    public static Unit Value => default;\n}"
  },
  {
    "path": "src/Serilog.Expressions/ParserConstruction/Parse.cs",
    "content": "﻿// Copyright 2016 Datalust, Superpower Contributors, Sprache Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.ParserConstruction.Display;\nusing Serilog.ParserConstruction.Model;\nusing Serilog.ParserConstruction.Util;\n\nnamespace Serilog.ParserConstruction;\n\n/// <summary>\n/// General parsing helper methods.\n/// </summary>\nstatic class Parse\n{\n    /// <summary>\n    /// Parse a sequence of similar operands connected by left-associative operators.\n    /// </summary>\n    /// <typeparam name=\"T\">The type being parsed.</typeparam>\n    /// <typeparam name=\"TOperator\">The type of the operator.</typeparam>\n    /// <typeparam name=\"TKind\">The kind of token being parsed.</typeparam>\n    /// <param name=\"operator\">A parser matching operators.</param>\n    /// <param name=\"operand\">A parser matching operands.</param>\n    /// <param name=\"apply\">A function combining an operator and two operands into the result.</param>\n    /// <returns>The result of calling <paramref name=\"apply\"/> successively on pairs of operands.</returns>\n    /// <seealso cref=\"Combinators.Chain{TKind, TResult,TOperator,TOperand}\"/>\n    public static TokenListParser<TKind, T> Chain<TKind, T, TOperator>(\n        TokenListParser<TKind, TOperator> @operator,\n        TokenListParser<TKind, T> operand,\n        Func<TOperator, T, T, T> apply)\n    {\n        return operand.Chain(@operator, operand, apply);\n    }\n\n    /// <summary>\n    /// Constructs a parser that will fail if the given parser succeeds,\n    /// and will succeed if the given parser fails. In any case, it won't\n    /// consume any input. It's like a negative look-ahead in a regular expression.\n    /// </summary>\n    /// <typeparam name=\"T\">The result type of the given parser.</typeparam>\n    /// <typeparam name=\"TKind\">The kind of token being parsed.</typeparam>\n    /// <param name=\"parser\">The parser to wrap</param>\n    /// <returns>A parser that is the negation of the given parser.</returns>\n    public static TokenListParser<TKind, Unit> Not<TKind, T>(TokenListParser<TKind, T> parser)\n    {\n        if (parser == null) throw new ArgumentNullException(nameof(parser));\n\n        return input =>\n        {\n            var result = parser(input);\n\n            if (result.HasValue)\n            {\n                // This is usually a success case for Not(), so the allocations here are a bit of a pity.\n\n                var current = input.ConsumeToken();\n                var last = result.Remainder.ConsumeToken();\n                if (current.HasValue)\n                {\n                    var span = last.HasValue ?\n                        current.Value.Span.Source!.Substring(current.Value.Position.Absolute, last.Value.Position.Absolute - current.Value.Position.Absolute) :\n                        current.Value.Span.Source!.Substring(current.Value.Position.Absolute);\n                    return TokenListParserResult.Empty<TKind, Unit>(input, $\"unexpected successful parsing of {Presentation.FormatLiteral(Friendly.Clip(span, 12))}\");\n                }\n\n                return TokenListParserResult.Empty<TKind, Unit>(input, \"unexpected successful parsing\");\n            }\n\n            return TokenListParserResult.Value(Unit.Value, input, input);\n        };\n    }\n\n    /// <summary>\n    /// Lazily construct a parser, so that circular dependencies are possible.\n    /// </summary>\n    /// <param name=\"reference\">A function creating the parser, when required.</param>\n    /// <typeparam name=\"T\">The type of value being parsed.</typeparam>\n    /// <typeparam name=\"TKind\">The kind of token being parsed.</typeparam>\n    /// <returns>A parser that lazily evaluates <paramref name=\"reference\"/>.</returns>\n    /// <exception cref=\"ArgumentNullException\"><paramref name=\"reference\"/> is null.</exception>\n    public static TokenListParser<TKind, T> Ref<TKind, T>(Func<TokenListParser<TKind, T>> reference)\n    {\n        if (reference == null) throw new ArgumentNullException(nameof(reference));\n\n        TokenListParser<TKind, T>? parser = null;\n\n        return i =>\n        {\n            parser ??= reference();\n\n            return parser(i);\n        };\n    }\n\n    /// <summary>\n    /// Construct a parser with a fixed value.\n    /// </summary>\n    /// <param name=\"value\">The value returned by the parser.</param>\n    /// <typeparam name=\"T\">The type of <paramref name=\"value\"/>.</typeparam>\n    /// <returns>The parser.</returns>\n    public static TextParser<T> Return<T>(T value)\n    {\n        return input => Result.Value(value, input, input);\n    }\n\n    /// <summary>\n    /// Construct a parser with a fixed value.\n    /// </summary>\n    /// <param name=\"value\">The value returned by the parser.</param>\n    /// <typeparam name=\"T\">The type of <paramref name=\"value\"/>.</typeparam>\n    /// <typeparam name=\"TKind\">The kind of token being parsed.</typeparam>\n    /// <returns>The parser.</returns>\n    public static TokenListParser<TKind, T> Return<TKind, T>(T value)\n    {\n        return input => TokenListParserResult.Value(value, input, input);\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/ParserConstruction/ParseException.cs",
    "content": "﻿// Copyright 2016 Datalust, Superpower Contributors, Sprache Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.ParserConstruction.Model;\n\n// ReSharper disable IntroduceOptionalParameters.Global, MemberCanBePrivate.Global, UnusedAutoPropertyAccessor.Global\n\nnamespace Serilog.ParserConstruction;\n\n/// <summary>\n/// Represents an error that occurs during parsing.\n/// </summary>\nclass ParseException : Exception\n{\n    /// <summary>\n    /// Initializes a new instance of the <see cref=\"ParseException\" /> class with a specified error message.\n    /// </summary>\n    /// <param name=\"message\">The message that describes the error.</param>\n    /// <param name=\"errorPosition\">The position of the error in the input text.</param>\n    public ParseException(string message, Position errorPosition) : this(message, errorPosition, null) { }\n\n    /// <summary>\n    /// Initializes a new instance of the <see cref=\"ParseException\" /> class with a specified error message.\n    /// </summary>\n    /// <param name=\"message\">The message that describes the error.</param>\n    /// <param name=\"errorPosition\">The position of the error in the input text.</param>\n    /// <param name=\"innerException\">The exception that is the cause of the current exception.</param>\n    public ParseException(string message, Position errorPosition, Exception? innerException) : base(message, innerException)\n    {\n        ErrorPosition = errorPosition;\n    }\n\n    /// <summary>\n    /// The position of the error in the input text, or <see cref=\"Position.Empty\"/> if no position is specified.\n    /// </summary>\n    public Position ErrorPosition { get; }\n}"
  },
  {
    "path": "src/Serilog.Expressions/ParserConstruction/ParserExtensions.cs",
    "content": "﻿// Copyright 2016 Datalust, Superpower Contributors, Sprache Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.ParserConstruction.Model;\n\nnamespace Serilog.ParserConstruction;\n\n/// <summary>\n/// Helper methods for working with parsers.\n/// </summary>\nstatic class ParserExtensions\n{\n    /// <summary>\n    /// Tries to parse the input without throwing an exception upon failure.\n    /// </summary>\n    /// <typeparam name=\"TKind\">The type of tokens consumed by the parser.</typeparam>\n    /// <typeparam name=\"T\">The type of the result.</typeparam>\n    /// <param name=\"parser\">The parser.</param>\n    /// <param name=\"input\">The input.</param>\n    /// <returns>The result of the parser</returns>\n    /// <exception cref=\"ArgumentNullException\">The parser or input is null.</exception>\n    public static TokenListParserResult<TKind, T> TryParse<TKind, T>(this TokenListParser<TKind, T> parser, TokenList<TKind> input)\n    {\n        if (parser == null) throw new ArgumentNullException(nameof(parser));\n\n        return parser(input);\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/ParserConstruction/Parsers/Character.cs",
    "content": "﻿// Copyright 2016 Datalust, Superpower Contributors, Sprache Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.ParserConstruction.Display;\nusing Serilog.ParserConstruction.Model;\n\nnamespace Serilog.ParserConstruction.Parsers;\n\n/// <summary>\n/// Parsers for matching individual characters.\n/// </summary>\nstatic class Character\n{\n    static TextParser<char> Matching(Func<char, bool> predicate, string[] expectations)\n    {\n        if (predicate == null) throw new ArgumentNullException(nameof(predicate));\n        if (expectations == null) throw new ArgumentNullException(nameof(expectations));\n\n        return input =>\n        {\n            var next = input.ConsumeChar();\n            if (!next.HasValue || !predicate(next.Value))\n                return Result.Empty<char>(input, expectations);\n\n            return next;\n        };\n    }\n\n    /// <summary>\n    /// Parse a single character matching <paramref name=\"predicate\"/>.\n    /// </summary>\n    public static TextParser<char> Matching(Func<char, bool> predicate, string name)\n    {\n        if (predicate == null) throw new ArgumentNullException(nameof(predicate));\n        if (name == null) throw new ArgumentNullException(nameof(name));\n\n        return Matching(predicate, [name]);\n    }\n\n    /// <summary>\n    /// Parse a single character except those matching <paramref name=\"predicate\"/>.\n    /// </summary>\n    /// <param name=\"predicate\">Characters not to match.</param>\n    /// <param name=\"description\">Description of characters that don't match.</param>\n    /// <returns>A parser for characters except those matching <paramref name=\"predicate\"/>.</returns>\n    static TextParser<char> Except(Func<char, bool> predicate, string description)\n    {\n        if (predicate == null) throw new ArgumentNullException(nameof(predicate));\n        if (description == null) throw new ArgumentNullException(nameof(description));\n\n        return Matching(c => !predicate(c), \"any character except \" + description);\n    }\n\n    /// <summary>\n    /// Parse a single specified character.\n    /// </summary>\n    public static TextParser<char> EqualTo(char ch)\n    {\n        return Matching(parsed => parsed == ch, Presentation.FormatLiteral(ch));\n    }\n\n    /// <summary>\n    /// Parse a single character except <paramref name=\"ch\"/>.\n    /// </summary>\n    public static TextParser<char> Except(char ch)\n    {\n        return Except(parsed => parsed == ch, Presentation.FormatLiteral(ch));\n    }\n    /// <summary>\n    /// Parse a digit.\n    /// </summary>\n    public static TextParser<char> Digit { get; } = Matching(char.IsDigit, \"digit\");\n}"
  },
  {
    "path": "src/Serilog.Expressions/ParserConstruction/Parsers/Numerics.cs",
    "content": "﻿// Copyright 2016 Datalust, Superpower Contributors, Sprache Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.ParserConstruction.Model;\nusing Serilog.ParserConstruction.Util;\n\nnamespace Serilog.ParserConstruction.Parsers;\n\n/// <summary>\n/// Parsers for numeric patterns.\n/// </summary>\n//* Fairly large amount of duplication/repetition here, due to the lack\n//* of generics over numbers in C#.\nstatic class Numerics\n{\n    static readonly string[] ExpectedDigit = [\"digit\"];\n    static readonly string[] ExpectedSignOrDigit = [\"sign\", \"digit\"];\n\n    /// <summary>\n    /// A string of digits, converted into a <see cref=\"uint\"/>.\n    /// </summary>\n    public static TextParser<uint> NaturalUInt32 { get; } = input =>\n    {\n        var next = input.ConsumeChar();\n\n        if (!next.HasValue || !CharInfo.IsLatinDigit(next.Value))\n            return Result.Empty<uint>(input, ExpectedDigit);\n\n        TextSpan remainder;\n        var val = 0u;\n        do\n        {\n            val = 10 * val + (uint)(next.Value - '0');\n            remainder = next.Remainder;\n            next = remainder.ConsumeChar();\n        } while (next.HasValue && CharInfo.IsLatinDigit(next.Value));\n\n        return Result.Value(val, input, remainder);\n    };\n\n    /// <summary>\n    /// A string of digits with an optional +/- sign.\n    /// </summary>\n    public static TextParser<TextSpan> Integer { get; } = input =>\n    {\n        var next = input.ConsumeChar();\n\n        if (!next.HasValue)\n            return Result.Empty<TextSpan>(input, ExpectedSignOrDigit);\n\n        if (next.Value is '-' or '+')\n            next = next.Remainder.ConsumeChar();\n\n        if (!next.HasValue || !CharInfo.IsLatinDigit(next.Value))\n            return Result.Empty<TextSpan>(input, ExpectedDigit);\n\n        TextSpan remainder;\n        do\n        {\n            remainder = next.Remainder;\n            next = remainder.ConsumeChar();\n        } while (next.HasValue && CharInfo.IsLatinDigit(next.Value));\n\n        return Result.Value(input.Until(remainder), input, remainder);\n    };\n}"
  },
  {
    "path": "src/Serilog.Expressions/ParserConstruction/Parsers/Span.cs",
    "content": "﻿// Copyright 2016 Datalust, Superpower Contributors, Sprache Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.ParserConstruction.Display;\nusing Serilog.ParserConstruction.Model;\n\nnamespace Serilog.ParserConstruction.Parsers;\n\n/// <summary>\n/// Parsers for spans of characters.\n/// </summary>\nstatic class Span\n{\n    /// <summary>\n    /// Match a span equal to <paramref name=\"text\"/>.\n    /// </summary>\n    /// <param name=\"text\">The text to match.</param>\n    /// <returns>The matched text.</returns>\n    public static TextParser<TextSpan> EqualTo(string text)\n    {\n        if (text == null) throw new ArgumentNullException(nameof(text));\n\n        var expectations = new[] { Presentation.FormatLiteral(text) };\n        return input =>\n        {\n            var remainder = input;\n            // ReSharper disable once ForCanBeConvertedToForeach\n            for (var i = 0; i < text.Length; ++i)\n            {\n                var ch = remainder.ConsumeChar();\n                if (!ch.HasValue || ch.Value != text[i])\n                {\n                    if (ch.Location == input)\n                        return Result.Empty<TextSpan>(ch.Location, expectations);\n\n                    return Result.Empty<TextSpan>(ch.Location, [Presentation.FormatLiteral(text[i])]);\n                }\n                remainder = ch.Remainder;\n            }\n            return Result.Value(input.Until(remainder), input, remainder);\n        };\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/ParserConstruction/Parsers/Token.cs",
    "content": "﻿// Copyright 2016 Datalust, Superpower Contributors, Sprache Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.ParserConstruction.Display;\nusing Serilog.ParserConstruction.Model;\n\nnamespace Serilog.ParserConstruction.Parsers;\n\n/// <summary>\n/// Parsers for matching individual tokens.\n/// </summary>\nstatic class Token\n{\n    /// <summary>\n    /// Parse a token of the kind <typeparamref name=\"TKind\"/>.\n    /// </summary>\n    /// <typeparam name=\"TKind\">The type of the token being matched.</typeparam>\n    /// <param name=\"kind\">The kind of token to match.</param>\n    /// <returns>The matched token.</returns>\n    // ReSharper disable once MemberCanBePrivate.Global\n    public static TokenListParser<TKind, Token<TKind>> EqualTo<TKind>(TKind kind)\n    {\n        var expectations = new[] { Presentation.FormatExpectation(kind) };\n\n        return input =>\n        {\n            var next = input.ConsumeToken();\n            if (!next.HasValue || !next.Value.Kind!.Equals(kind))\n                return TokenListParserResult.Empty<TKind, Token<TKind>>(input, expectations);\n\n            return next;\n        };\n    }\n\n    /// <summary>\n    /// Parse a sequence of tokens of the kind <typeparamref name=\"TKind\"/>.\n    /// </summary>\n    /// <typeparam name=\"TKind\">The type of the tokens being matched.</typeparam>\n    /// <param name=\"kinds\">The kinds of token to match, once each in order.</param>\n    /// <returns>The matched tokens.</returns>\n    public static TokenListParser<TKind, Token<TKind>[]> Sequence<TKind>(params TKind[] kinds)\n    {\n        if (kinds == null) throw new ArgumentNullException(nameof(kinds));\n\n        TokenListParser<TKind, Token<TKind>[]> result = input => TokenListParserResult.Value(new Token<TKind>[kinds.Length], input, input);\n        for (var i = 0; i < kinds.Length; ++i)\n        {\n            var token = EqualTo(kinds[i]);\n            var index = i;\n            result = result.Then(arr => token.Select(t => { arr[index] = t; return arr; }));\n        }\n        return result;\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/ParserConstruction/README.md",
    "content": "﻿# Superpower\n\nThe classes in this namespace are from [Superpower](https://github.com/datalust/superpower).\n\nThey are included here because _Serilog.Expressions_ aims to eventually be integrated at a lower\nlevel in the Serilog ecosystem, where being dependency-free is advantageous.\n\n_Serilog.Expressions_ only uses a small slice of Superpower; if something you need appears to\nbe missing, it's probably been shaken out and can be retrieved again from the original codebase\nfor inclusion.\n\nThe Superpower tests have not been brought across; as only code used in _Serilog.Expressions_\nremains here, we should be able to achieve good test coverage by testing the expression parser\nimplementation.\n\n## License\n\nApache License\nVersion 2.0, January 2004\nhttp://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n   \"License\" shall mean the terms and conditions for use, reproduction,\n   and distribution as defined by Sections 1 through 9 of this document.\n\n   \"Licensor\" shall mean the copyright owner or entity authorized by\n   the copyright owner that is granting the License.\n\n   \"Legal Entity\" shall mean the union of the acting entity and all\n   other entities that control, are controlled by, or are under common\n   control with that entity. For the purposes of this definition,\n   \"control\" means (i) the power, direct or indirect, to cause the\n   direction or management of such entity, whether by contract or\n   otherwise, or (ii) ownership of fifty percent (50%) or more of the\n   outstanding shares, or (iii) beneficial ownership of such entity.\n\n   \"You\" (or \"Your\") shall mean an individual or Legal Entity\n   exercising permissions granted by this License.\n\n   \"Source\" form shall mean the preferred form for making modifications,\n   including but not limited to software source code, documentation\n   source, and configuration files.\n\n   \"Object\" form shall mean any form resulting from mechanical\n   transformation or translation of a Source form, including but\n   not limited to compiled object code, generated documentation,\n   and conversions to other media types.\n\n   \"Work\" shall mean the work of authorship, whether in Source or\n   Object form, made available under the License, as indicated by a\n   copyright notice that is included in or attached to the work\n   (an example is provided in the Appendix below).\n\n   \"Derivative Works\" shall mean any work, whether in Source or Object\n   form, that is based on (or derived from) the Work and for which the\n   editorial revisions, annotations, elaborations, or other modifications\n   represent, as a whole, an original work of authorship. For the purposes\n   of this License, Derivative Works shall not include works that remain\n   separable from, or merely link (or bind by name) to the interfaces of,\n   the Work and Derivative Works thereof.\n\n   \"Contribution\" shall mean any work of authorship, including\n   the original version of the Work and any modifications or additions\n   to that Work or Derivative Works thereof, that is intentionally\n   submitted to Licensor for inclusion in the Work by the copyright owner\n   or by an individual or Legal Entity authorized to submit on behalf of\n   the copyright owner. For the purposes of this definition, \"submitted\"\n   means any form of electronic, verbal, or written communication sent\n   to the Licensor or its representatives, including but not limited to\n   communication on electronic mailing lists, source code control systems,\n   and issue tracking systems that are managed by, or on behalf of, the\n   Licensor for the purpose of discussing and improving the Work, but\n   excluding communication that is conspicuously marked or otherwise\n   designated in writing by the copyright owner as \"Not a Contribution.\"\n\n   \"Contributor\" shall mean Licensor and any individual or Legal Entity\n   on behalf of whom a Contribution has been received by Licensor and\n   subsequently incorporated within the Work.\n\n2. Grant of Copyright License. Subject to the terms and conditions of\n   this License, each Contributor hereby grants to You a perpetual,\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n   copyright license to reproduce, prepare Derivative Works of,\n   publicly display, publicly perform, sublicense, and distribute the\n   Work and such Derivative Works in Source or Object form.\n\n3. Grant of Patent License. Subject to the terms and conditions of\n   this License, each Contributor hereby grants to You a perpetual,\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n   (except as stated in this section) patent license to make, have made,\n   use, offer to sell, sell, import, and otherwise transfer the Work,\n   where such license applies only to those patent claims licensable\n   by such Contributor that are necessarily infringed by their\n   Contribution(s) alone or by combination of their Contribution(s)\n   with the Work to which such Contribution(s) was submitted. If You\n   institute patent litigation against any entity (including a\n   cross-claim or counterclaim in a lawsuit) alleging that the Work\n   or a Contribution incorporated within the Work constitutes direct\n   or contributory patent infringement, then any patent licenses\n   granted to You under this License for that Work shall terminate\n   as of the date such litigation is filed.\n\n4. Redistribution. You may reproduce and distribute copies of the\n   Work or Derivative Works thereof in any medium, with or without\n   modifications, and in Source or Object form, provided that You\n   meet the following conditions:\n\n   (a) You must give any other recipients of the Work or\n   Derivative Works a copy of this License; and\n\n   (b) You must cause any modified files to carry prominent notices\n   stating that You changed the files; and\n\n   (c) You must retain, in the Source form of any Derivative Works\n   that You distribute, all copyright, patent, trademark, and\n   attribution notices from the Source form of the Work,\n   excluding those notices that do not pertain to any part of\n   the Derivative Works; and\n\n   (d) If the Work includes a \"NOTICE\" text file as part of its\n   distribution, then any Derivative Works that You distribute must\n   include a readable copy of the attribution notices contained\n   within such NOTICE file, excluding those notices that do not\n   pertain to any part of the Derivative Works, in at least one\n   of the following places: within a NOTICE text file distributed\n   as part of the Derivative Works; within the Source form or\n   documentation, if provided along with the Derivative Works; or,\n   within a display generated by the Derivative Works, if and\n   wherever such third-party notices normally appear. The contents\n   of the NOTICE file are for informational purposes only and\n   do not modify the License. You may add Your own attribution\n   notices within Derivative Works that You distribute, alongside\n   or as an addendum to the NOTICE text from the Work, provided\n   that such additional attribution notices cannot be construed\n   as modifying the License.\n\n   You may add Your own copyright statement to Your modifications and\n   may provide additional or different license terms and conditions\n   for use, reproduction, or distribution of Your modifications, or\n   for any such Derivative Works as a whole, provided Your use,\n   reproduction, and distribution of the Work otherwise complies with\n   the conditions stated in this License.\n\n5. Submission of Contributions. Unless You explicitly state otherwise,\n   any Contribution intentionally submitted for inclusion in the Work\n   by You to the Licensor shall be under the terms and conditions of\n   this License, without any additional terms or conditions.\n   Notwithstanding the above, nothing herein shall supersede or modify\n   the terms of any separate license agreement you may have executed\n   with Licensor regarding such Contributions.\n\n6. Trademarks. This License does not grant permission to use the trade\n   names, trademarks, service marks, or product names of the Licensor,\n   except as required for reasonable and customary use in describing the\n   origin of the Work and reproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty. Unless required by applicable law or\n   agreed to in writing, Licensor provides the Work (and each\n   Contributor provides its Contributions) on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n   implied, including, without limitation, any warranties or conditions\n   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n   PARTICULAR PURPOSE. You are solely responsible for determining the\n   appropriateness of using or redistributing the Work and assume any\n   risks associated with Your exercise of permissions under this License.\n\n8. Limitation of Liability. In no event and under no legal theory,\n   whether in tort (including negligence), contract, or otherwise,\n   unless required by applicable law (such as deliberate and grossly\n   negligent acts) or agreed to in writing, shall any Contributor be\n   liable to You for damages, including any direct, indirect, special,\n   incidental, or consequential damages of any character arising as a\n   result of this License or out of the use or inability to use the\n   Work (including but not limited to damages for loss of goodwill,\n   work stoppage, computer failure or malfunction, or any and all\n   other commercial damages or losses), even if such Contributor\n   has been advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability. While redistributing\n   the Work or Derivative Works thereof, You may choose to offer,\n   and charge a fee for, acceptance of support, warranty, indemnity,\n   or other liability obligations and/or rights consistent with this\n   License. However, in accepting such obligations, You may act only\n   on Your own behalf and on Your sole responsibility, not on behalf\n   of any other Contributor, and only if You agree to indemnify,\n   defend, and hold each Contributor harmless for any liability\n   incurred by, or claims asserted against, such Contributor by reason\n   of your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"{}\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\nCopyright {yyyy} {name of copyright owner}\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n"
  },
  {
    "path": "src/Serilog.Expressions/ParserConstruction/TextParser`1.cs",
    "content": "// Copyright 2016 Datalust, Superpower Contributors, Sprache Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.ParserConstruction.Model;\n\nnamespace Serilog.ParserConstruction;\n\n/// <summary>\n/// A parser that consumes text from a string span.\n/// </summary>\n/// <typeparam name=\"T\">The type of values produced by the parser.</typeparam>\n/// <param name=\"input\">The span of text to parse.</param>\n/// <returns>A result with a parsed value, or an empty result indicating error.</returns>\ndelegate Result<T> TextParser<T>(TextSpan input);"
  },
  {
    "path": "src/Serilog.Expressions/ParserConstruction/TokenListParser`2.cs",
    "content": "// Copyright 2016 Datalust, Superpower Contributors, Sprache Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.ParserConstruction.Model;\n\nnamespace Serilog.ParserConstruction;\n\n/// <summary>\n/// A parser that consumes elements from a list of tokens.\n/// </summary>\n/// <typeparam name=\"T\">The type of values produced by the parser.</typeparam>\n/// <typeparam name=\"TKind\">The type of tokens being parsed.</typeparam>\n/// <param name=\"input\">The list of tokens to parse.</param>\n/// <returns>A result with a parsed value, or an empty result indicating error.</returns>\ndelegate TokenListParserResult<TKind, T> TokenListParser<TKind, T>(TokenList<TKind> input);"
  },
  {
    "path": "src/Serilog.Expressions/ParserConstruction/Tokenizer`1.cs",
    "content": "// Copyright 2016-2018 Datalust, Superpower Contributors, Sprache Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.ParserConstruction.Display;\nusing Serilog.ParserConstruction.Model;\n\nnamespace Serilog.ParserConstruction;\n\n/// <summary>\n/// Base class for tokenizers, types whose instances convert strings into lists of tokens.\n/// </summary>\n/// <typeparam name=\"TKind\">The kind of tokens produced.</typeparam>\nabstract class Tokenizer<TKind>\n{\n    /// <summary>\n    /// Tokenize <paramref name=\"source\"/>.\n    /// </summary>\n    /// <param name=\"source\">The source to tokenize.</param>\n    /// <returns>The list of tokens or an error.</returns>\n    /// <exception cref=\"ParseException\">Tokenization failed.</exception>\n    public TokenList<TKind> Tokenize(string source)\n    {\n        var result = TryTokenize(source);\n        if (result.HasValue)\n            return result.Value;\n\n        throw new ParseException(result.ToString(), result.ErrorPosition);\n    }\n\n    /// <summary>\n    /// Tokenize <paramref name=\"source\"/>.\n    /// </summary>\n    /// <param name=\"source\">The source to tokenize.</param>\n    /// <returns>A result with the list of tokens or an error.</returns>\n    /// <exception cref=\"ArgumentNullException\"><paramref name=\"source\"/> is null.</exception>\n    /// <exception cref=\"ParseException\">The tokenizer could not correctly perform tokenization.</exception>\n    public Result<TokenList<TKind>> TryTokenize(string source)\n    {\n        if (source == null) throw new ArgumentNullException(nameof(source));\n\n        var sourceSpan = new TextSpan(source);\n        var remainder = sourceSpan;\n        var results = new List<Token<TKind>>();\n        foreach (var result in Tokenize(sourceSpan))\n        {\n            if (!result.HasValue)\n                return Result.CastEmpty<TKind, TokenList<TKind>>(result);\n\n            if (result.Remainder == remainder) // Broken parser, not a failed parsing.\n                throw new ParseException($\"Zero-width tokens are not supported; token {Presentation.FormatExpectation(result.Value)} at position {result.Location.Position}.\", result.Location.Position);\n\n            remainder = result.Remainder;\n            var token = new Token<TKind>(result.Value, result.Location.Until(result.Remainder));\n            results.Add(token);\n        }\n\n        var value = new TokenList<TKind>(results.ToArray());\n        return Result.Value(value, sourceSpan, remainder);\n    }\n\n    /// <summary>\n    /// Subclasses should override to perform tokenization.\n    /// </summary>\n    /// <param name=\"span\">The input span to tokenize.</param>\n    /// <returns>A list of parsed tokens.</returns>\n    protected abstract IEnumerable<Result<TKind>> Tokenize(TextSpan span);\n\n    /// <summary>\n    /// Advance until the first non-whitespace character is encountered.\n    /// </summary>\n    /// <param name=\"span\">The span to advance from.</param>\n    /// <returns>A result with the first non-whitespace character.</returns>\n    protected static Result<char> SkipWhiteSpace(TextSpan span)\n    {\n        var next = span.ConsumeChar();\n        while (next.HasValue && char.IsWhiteSpace(next.Value))\n        {\n            next = next.Remainder.ConsumeChar();\n        }\n        return next;\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/ParserConstruction/Util/CharInfo.cs",
    "content": "﻿// Copyright 2018 Datalust, Superpower Contributors, Sprache Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nnamespace Serilog.ParserConstruction.Util;\n\nstatic class CharInfo\n{\n    public static bool IsLatinDigit(char ch)\n    {\n        return ch is >= '0' and <= '9';\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/ParserConstruction/Util/Friendly.cs",
    "content": "﻿// Copyright 2016 Datalust, Superpower Contributors, Sprache Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nnamespace Serilog.ParserConstruction.Util;\n\nstatic class Friendly\n{\n    public static string List(IEnumerable<string> items)\n    {\n        if (items == null) throw new ArgumentNullException(nameof(items));\n\n        // Keep the order stable\n        var seen = new HashSet<string>();\n        var unique = new List<string>();\n        foreach (var item in items)\n        {\n            if (seen.Contains(item)) continue;\n            seen.Add(item);\n            unique.Add(item);\n        }\n\n        if (unique.Count == 0)\n            throw new ArgumentException(\"Friendly list formatting requires at least one element.\", nameof(items));\n\n        if (unique.Count == 1)\n            return unique.Single();\n\n        return $\"{string.Join(\", \", unique.Take(unique.Count - 1))} or {unique.Last()}\";\n    }\n\n    public static string Clip(string value, int maxLength)\n    {\n        if (value.Length > maxLength)\n            return value.Substring(0, maxLength - 3) + \"...\";\n        return value;\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Pipeline/ComputedPropertyEnricher.cs",
    "content": "// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Core;\nusing Serilog.Events;\nusing Serilog.Expressions;\n\nnamespace Serilog.Pipeline;\n\nclass ComputedPropertyEnricher : ILogEventEnricher\n{\n    readonly string _propertyName;\n    readonly CompiledExpression _computeValue;\n\n    public ComputedPropertyEnricher(string propertyName, CompiledExpression computeValue)\n    {\n        _propertyName = propertyName ?? throw new ArgumentNullException(nameof(propertyName));\n        _computeValue = computeValue ?? throw new ArgumentNullException(nameof(computeValue));\n    }\n\n    public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)\n    {\n        var value = _computeValue(logEvent);\n        if (value != null)\n            logEvent.AddOrUpdateProperty(new(_propertyName, value));\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Properties/AssemblyInfo.cs",
    "content": "﻿using System.Runtime.CompilerServices;\n\n[assembly: CLSCompliant(true)]\n\n[assembly: InternalsVisibleTo(\"Serilog.Expressions.Tests, PublicKey=\" +\n                              \"0024000004800000940000000602000000240000525341310004000001000100fb8d13fd344a1c\" +\n                              \"6fe0fe83ef33c1080bf30690765bc6eb0df26ebfdf8f21670c64265b30db09f73a0dea5b3db4c9\" +\n                              \"d18dbf6d5a25af5ce9016f281014d79dc3b4201ac646c451830fc7e61a2dfd633d34c39f87b818\" +\n                              \"94191652df5ac63cc40c77f3542f702bda692e6e8a9158353df189007a49da0f3cfd55eb250066\" +\n                              \"b19485ec\")]\n"
  },
  {
    "path": "src/Serilog.Expressions/Serilog.Expressions.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <Description>An embeddable mini-language for filtering, enriching, and formatting Serilog\n      events, ideal for use with JSON or XML configuration.</Description>\n    <Authors>Serilog Contributors</Authors>\n    <!-- .NET Framework version targeting is frozen at these two TFMs. -->\n    <TargetFrameworks Condition=\" '$(OS)' == 'Windows_NT'\">net471;net462</TargetFrameworks>\n    <!-- Policy is to trim TFM-specific builds to `netstandard2.0`, `net6.0`,\n    all active LTS versions, and optionally the latest RTM version, when releasing new\n    major Serilog versions. -->\n    <TargetFrameworks>$(TargetFrameworks);net9.0;net8.0;net6.0;netstandard2.0</TargetFrameworks>\n    <PackageTags>serilog</PackageTags>\n    <PackageProjectUrl>https://github.com/serilog/serilog-expressions</PackageProjectUrl>\n    <PackageIcon>icon.png</PackageIcon>\n    <PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>\n    <RootNamespace>Serilog</RootNamespace>\n    <PackageReadmeFile>README.md</PackageReadmeFile>\n  </PropertyGroup>\n\n  <PropertyGroup Condition=\" '$(TargetFramework)' == 'netstandard2.0' \">\n    <DefineConstants>$(DefineConstants);NO_CI_STRING_CONTAINS</DefineConstants>\n  </PropertyGroup>\n\n  <PropertyGroup Condition=\" '$(TargetFramework)' == 'net471' \">\n    <DefineConstants>$(DefineConstants);NO_CI_STRING_CONTAINS</DefineConstants>\n  </PropertyGroup>\n\n  <PropertyGroup Condition=\" '$(TargetFramework)' == 'net462' \">\n    <DefineConstants>$(DefineConstants);NO_CI_STRING_CONTAINS</DefineConstants>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Serilog\" Version=\"4.2.0\" />\n    <PackageReference Include=\"Nullable\" Version=\"1.3.1\" PrivateAssets=\"All\" />\n    <None Include=\"..\\..\\assets\\icon.png\" Pack=\"true\" Visible=\"false\" PackagePath=\"\" />\n    <None Include=\"..\\..\\README.md\" Pack=\"true\" Visible=\"false\" PackagePath=\"\" />\n  </ItemGroup>\n  \n</Project>\n"
  },
  {
    "path": "src/Serilog.Expressions/Templates/Ast/Conditional.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Expressions.Ast;\n\nnamespace Serilog.Templates.Ast;\n\nclass Conditional : Template\n{\n    public Expression Condition { get; }\n    public Template Consequent { get; }\n    public Template? Alternative { get; }\n\n    public Conditional(Expression condition, Template consequent, Template? alternative)\n    {\n        Condition = condition ?? throw new ArgumentNullException(nameof(condition));\n        Consequent = consequent ?? throw new ArgumentNullException(nameof(consequent));\n        Alternative = alternative;\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Templates/Ast/FormattedExpression.cs",
    "content": "// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Expressions.Ast;\nusing Serilog.Parsing;\n\nnamespace Serilog.Templates.Ast;\n\nclass FormattedExpression : Template\n{\n    public Expression Expression { get; }\n    public string? Format { get; }\n    public Alignment? Alignment { get; }\n\n    public FormattedExpression(Expression expression, string? format, Alignment? alignment)\n    {\n        Expression = expression ?? throw new ArgumentNullException(nameof(expression));\n        Format = format;\n        Alignment = alignment;\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Templates/Ast/LiteralText.cs",
    "content": "// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nnamespace Serilog.Templates.Ast;\n\nclass LiteralText : Template\n{\n    public string Text { get; }\n\n    public LiteralText(string text)\n    {\n        Text = text ?? throw new ArgumentNullException(nameof(text));\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Templates/Ast/Repetition.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Expressions.Ast;\n\nnamespace Serilog.Templates.Ast;\n\nclass Repetition: Template\n{\n    public Expression Enumerable { get; }\n    public string[] BindingNames { get; }\n    public Template Body { get; }\n    public Template? Delimiter { get; }\n    public Template? Alternative { get; }\n\n    public Repetition(\n        Expression enumerable,\n        string[] bindingNames,\n        Template body,\n        Template? delimiter,\n        Template? alternative)\n    {\n        Enumerable = enumerable ?? throw new ArgumentNullException(nameof(enumerable));\n        BindingNames = bindingNames;\n        Body = body ?? throw new ArgumentNullException(nameof(body));\n        Delimiter = delimiter;\n        Alternative = alternative;\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Templates/Ast/Template.cs",
    "content": "// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nnamespace Serilog.Templates.Ast;\n\nabstract class Template;"
  },
  {
    "path": "src/Serilog.Expressions/Templates/Ast/TemplateBlock.cs",
    "content": "// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nnamespace Serilog.Templates.Ast;\n\nclass TemplateBlock : Template\n{\n    public Template[] Elements { get; }\n\n    public TemplateBlock(Template[] elements)\n    {\n        Elements = elements ?? throw new ArgumentNullException(nameof(elements));\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Templates/Compilation/CompiledConditional.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Expressions;\n\nnamespace Serilog.Templates.Compilation;\n\nclass CompiledConditional : CompiledTemplate\n{\n    readonly Evaluatable _condition;\n    readonly CompiledTemplate _consequent;\n    readonly CompiledTemplate? _alternative;\n\n    public CompiledConditional(Evaluatable condition, CompiledTemplate consequent, CompiledTemplate? alternative)\n    {\n        _condition = condition ?? throw new ArgumentNullException(nameof(condition));\n        _consequent = consequent ?? throw new ArgumentNullException(nameof(consequent));\n        _alternative = alternative;\n    }\n\n    public override void Evaluate(EvaluationContext ctx, TextWriter output)\n    {\n        if (ExpressionResult.IsTrue(_condition.Invoke(ctx)))\n            _consequent.Evaluate(ctx, output);\n        else\n            _alternative?.Evaluate(ctx, output);\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Templates/Compilation/CompiledExceptionToken.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Expressions;\nusing Serilog.Templates.Themes;\n\nnamespace Serilog.Templates.Compilation;\n\nclass CompiledExceptionToken : CompiledTemplate\n{\n    const string StackFrameLinePrefix = \"   \";\n\n    readonly Style _text, _secondaryText;\n\n    public CompiledExceptionToken(TemplateTheme theme)\n    {\n        _text = theme.GetStyle(TemplateThemeStyle.Text);\n        _secondaryText = theme.GetStyle(TemplateThemeStyle.SecondaryText);\n    }\n\n    public override void Evaluate(EvaluationContext ctx, TextWriter output)\n    {\n        // Padding and alignment are not applied by this renderer.\n\n        if (ctx.LogEvent.Exception is null)\n            return;\n\n        StringReader lines;\n        try\n        {\n            lines = new StringReader(ctx.LogEvent.Exception.ToString());\n        }\n        catch (Exception ex)\n        {\n            lines = new StringReader(\n                $\"[Exception.ToString() failed: {ex.Message}] Original exception type: {ctx.LogEvent.Exception.GetType().FullName}{Environment.NewLine}\");\n        }\n\n        while (lines.ReadLine() is { } nextLine)\n        {\n            var style = nextLine.StartsWith(StackFrameLinePrefix) ? _secondaryText : _text;\n            var _ = 0;\n            using (style.Set(output, ref _))\n                output.WriteLine(nextLine);\n        }\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Templates/Compilation/CompiledFormattedExpression.cs",
    "content": "// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Events;\nusing Serilog.Expressions;\nusing Serilog.Parsing;\nusing Serilog.Templates.Rendering;\nusing Serilog.Templates.Themes;\n\nnamespace Serilog.Templates.Compilation;\n\nclass CompiledFormattedExpression : CompiledTemplate\n{\n    readonly ThemedJsonValueFormatter _jsonFormatter;\n    readonly Evaluatable _expression;\n    readonly string? _format;\n    readonly Alignment? _alignment;\n    readonly IFormatProvider? _formatProvider;\n    readonly Style _secondaryText;\n\n    public CompiledFormattedExpression(Evaluatable expression, string? format, Alignment? alignment, IFormatProvider? formatProvider, TemplateTheme theme)\n    {\n        _expression = expression ?? throw new ArgumentNullException(nameof(expression));\n        _format = format;\n        _alignment = alignment;\n        _formatProvider = formatProvider;\n        _secondaryText = theme.GetStyle(TemplateThemeStyle.SecondaryText);\n        _jsonFormatter = new(theme);\n    }\n\n    public override void Evaluate(EvaluationContext ctx, TextWriter output)\n    {\n        var invisibleCharacterCount = 0;\n\n        if (_alignment == null)\n        {\n            EvaluateUnaligned(ctx, output, _formatProvider, ref invisibleCharacterCount);\n        }\n        else\n        {\n            var writer = new StringWriter();\n            EvaluateUnaligned(ctx, writer, _formatProvider, ref invisibleCharacterCount);\n            Padding.Apply(output, writer.ToString(), _alignment.Value.Widen(invisibleCharacterCount));\n        }\n    }\n\n    void EvaluateUnaligned(EvaluationContext ctx, TextWriter output, IFormatProvider? formatProvider, ref int invisibleCharacterCount)\n    {\n        var value = _expression(ctx);\n        if (value == null)\n            return; // Undefined is empty\n\n        if (value is ScalarValue scalar)\n        {\n            if (scalar.Value is null)\n                return; // Null is empty\n\n            using var style = _secondaryText.Set(output, ref invisibleCharacterCount);\n\n            if (scalar.Value is IFormattable fmt)\n                output.Write(fmt.ToString(_format, formatProvider));\n            else\n                output.Write(scalar.Value.ToString());\n        }\n        else\n        {\n            invisibleCharacterCount += _jsonFormatter.Format(value, output);\n        }\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Templates/Compilation/CompiledLevelToken.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Expressions;\nusing Serilog.Parsing;\nusing Serilog.Templates.Rendering;\nusing Serilog.Templates.Themes;\n\nnamespace Serilog.Templates.Compilation;\n\nclass CompiledLevelToken : CompiledTemplate\n{\n    readonly string? _format;\n    readonly Alignment? _alignment;\n    readonly Style[] _levelStyles;\n\n    public CompiledLevelToken(string? format, Alignment? alignment, TemplateTheme theme)\n    {\n        _format = format;\n        _alignment = alignment;\n        _levelStyles =\n        [\n            theme.GetStyle(TemplateThemeStyle.LevelVerbose),\n            theme.GetStyle(TemplateThemeStyle.LevelDebug),\n            theme.GetStyle(TemplateThemeStyle.LevelInformation),\n            theme.GetStyle(TemplateThemeStyle.LevelWarning),\n            theme.GetStyle(TemplateThemeStyle.LevelError),\n            theme.GetStyle(TemplateThemeStyle.LevelFatal)\n        ];\n    }\n\n    public override void Evaluate(EvaluationContext ctx, TextWriter output)\n    {\n        var invisibleCharacterCount = 0;\n\n        if (_alignment == null)\n        {\n            EvaluateUnaligned(ctx, output, ref invisibleCharacterCount);\n        }\n        else\n        {\n            var writer = new StringWriter();\n            EvaluateUnaligned(ctx, writer, ref invisibleCharacterCount);\n            Padding.Apply(output, writer.ToString(), _alignment.Value.Widen(invisibleCharacterCount));\n        }\n    }\n\n    void EvaluateUnaligned(EvaluationContext ctx, TextWriter output, ref int invisibleCharacterCount)\n    {\n        var levelIndex = (int) ctx.LogEvent.Level;\n        if (levelIndex < 0 || levelIndex >= _levelStyles.Length)\n            return;\n\n        using var _ = _levelStyles[levelIndex].Set(output, ref invisibleCharacterCount);\n        output.Write(LevelRenderer.GetLevelMoniker(ctx.LogEvent.Level, _format));\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Templates/Compilation/CompiledLiteralText.cs",
    "content": "// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Expressions;\nusing Serilog.Templates.Themes;\n\nnamespace Serilog.Templates.Compilation;\n\nclass CompiledLiteralText : CompiledTemplate\n{\n    readonly string _text;\n    readonly Style _style;\n\n    public CompiledLiteralText(string text, TemplateTheme theme)\n    {\n        _text = text ?? throw new ArgumentNullException(nameof(text));\n        _style = theme.GetStyle(TemplateThemeStyle.TertiaryText);\n    }\n\n    public override void Evaluate(EvaluationContext ctx, TextWriter output)\n    {\n        var _ = 0;\n        using (_style.Set(output, ref _))\n            output.Write(_text);\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Templates/Compilation/CompiledMessageToken.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Events;\nusing Serilog.Expressions;\nusing Serilog.Parsing;\nusing Serilog.Templates.Rendering;\nusing Serilog.Templates.Themes;\n\nnamespace Serilog.Templates.Compilation;\n\nclass CompiledMessageToken : CompiledTemplate\n{\n    readonly IFormatProvider? _formatProvider;\n    readonly Alignment? _alignment;\n    readonly Style _text, _invalid, _null, _bool, _string, _num, _scalar;\n    readonly ThemedJsonValueFormatter _jsonFormatter;\n\n    public CompiledMessageToken(IFormatProvider? formatProvider, Alignment? alignment, TemplateTheme theme)\n    {\n        _formatProvider = formatProvider;\n        _alignment = alignment;\n        _text = theme.GetStyle(TemplateThemeStyle.Text);\n        _null = theme.GetStyle(TemplateThemeStyle.Null);\n        _bool = theme.GetStyle(TemplateThemeStyle.Boolean);\n        _num = theme.GetStyle(TemplateThemeStyle.Number);\n        _string = theme.GetStyle(TemplateThemeStyle.String);\n        _scalar = theme.GetStyle(TemplateThemeStyle.Scalar);\n        _invalid = theme.GetStyle(TemplateThemeStyle.Invalid);\n        _jsonFormatter = new(theme);\n    }\n\n    public override void Evaluate(EvaluationContext ctx, TextWriter output)\n    {\n        var invisibleCharacterCount = 0;\n\n        if (_alignment == null)\n        {\n            EvaluateUnaligned(ctx, output, ref invisibleCharacterCount);\n        }\n        else\n        {\n            var writer = new StringWriter();\n            EvaluateUnaligned(ctx, writer, ref invisibleCharacterCount);\n            Padding.Apply(output, writer.ToString(), _alignment.Value.Widen(invisibleCharacterCount));\n        }\n    }\n\n    void EvaluateUnaligned(EvaluationContext ctx, TextWriter output, ref int invisibleCharacterCount)\n    {\n        foreach (var token in ctx.LogEvent.MessageTemplate.Tokens)\n        {\n            switch (token)\n            {\n                case TextToken tt:\n                {\n                    using var _ = _text.Set(output, ref invisibleCharacterCount);\n                    output.Write(tt.Text);\n                    break;\n                }\n                case PropertyToken pt:\n                {\n                    EvaluateProperty(ctx.LogEvent.Properties, pt, output, ref invisibleCharacterCount);\n                    break;\n                }\n                default:\n                {\n                    output.Write(token);\n                    break;\n                }\n            }\n        }\n    }\n\n    void EvaluateProperty(IReadOnlyDictionary<string,LogEventPropertyValue> properties, PropertyToken pt, TextWriter output, ref int invisibleCharacterCount)\n    {\n        if (!properties.TryGetValue(pt.PropertyName, out var value))\n        {\n            using var _ = _invalid.Set(output, ref invisibleCharacterCount);\n            output.Write(pt.ToString());\n            return;\n        }\n\n        if (pt.Alignment is null)\n        {\n            EvaluatePropertyUnaligned(value, output, pt.Format, ref invisibleCharacterCount);\n            return;\n        }\n\n        var buffer = new StringWriter();\n        var resultInvisibleCharacters = 0;\n\n        EvaluatePropertyUnaligned(value, buffer, pt.Format, ref resultInvisibleCharacters);\n\n        var result = buffer.ToString();\n        invisibleCharacterCount += resultInvisibleCharacters;\n\n        if (result.Length - resultInvisibleCharacters >= pt.Alignment.Value.Width)\n            output.Write(result);\n        else\n            Padding.Apply(output, result, pt.Alignment.Value.Widen(resultInvisibleCharacters));\n    }\n\n    void EvaluatePropertyUnaligned(LogEventPropertyValue propertyValue, TextWriter output, string? format, ref int invisibleCharacterCount)\n    {\n        if (propertyValue is not ScalarValue scalar)\n        {\n            invisibleCharacterCount += _jsonFormatter.Format(propertyValue, output);\n            return;\n        }\n\n        var value = scalar.Value;\n\n        if (value == null)\n        {\n            using (_null.Set(output, ref invisibleCharacterCount))\n                output.Write(\"null\");\n            return;\n        }\n\n        if (value is string str)\n        {\n            using (_string.Set(output, ref invisibleCharacterCount))\n                output.Write(str);\n            return;\n        }\n\n        if (value is ValueType)\n        {\n            if (value is int or uint or long or ulong or decimal or byte or sbyte or short or ushort)\n            {\n                using (_num.Set(output, ref invisibleCharacterCount))\n                    output.Write(((IFormattable)value).ToString(format, _formatProvider));\n                return;\n            }\n\n            if (value is double d)\n            {\n                using (_num.Set(output, ref invisibleCharacterCount))\n                    output.Write(d.ToString(format, _formatProvider));\n                return;\n            }\n\n            if (value is float f)\n            {\n                using (_num.Set(output, ref invisibleCharacterCount))\n                    output.Write(f.ToString(format, _formatProvider));\n                return;\n            }\n\n            if (value is bool b)\n            {\n                using (_bool.Set(output, ref invisibleCharacterCount))\n                    output.Write(b);\n                return;\n            }\n        }\n\n        if (value is IFormattable formattable)\n        {\n            using (_scalar.Set(output, ref invisibleCharacterCount))\n                output.Write(formattable.ToString(format, _formatProvider));\n            return;\n        }\n\n        using (_scalar.Set(output, ref invisibleCharacterCount))\n            output.Write(value);\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Templates/Compilation/CompiledRepetition.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Events;\nusing Serilog.Expressions;\nusing Serilog.Expressions.Runtime;\n\nnamespace Serilog.Templates.Compilation;\n\nclass CompiledRepetition : CompiledTemplate\n{\n    readonly Evaluatable _enumerable;\n    readonly string? _keyOrElementName;\n    readonly string? _valueOrIndexName;\n    readonly CompiledTemplate _body;\n    readonly CompiledTemplate? _delimiter;\n    readonly CompiledTemplate? _alternative;\n\n    public CompiledRepetition(\n        Evaluatable enumerable,\n        string? keyOrElementName,\n        string? valueOrIndexName,\n        CompiledTemplate body,\n        CompiledTemplate? delimiter,\n        CompiledTemplate? alternative)\n    {\n        _enumerable = enumerable;\n        _keyOrElementName = keyOrElementName;\n        _valueOrIndexName = valueOrIndexName;\n        _body = body;\n        _delimiter = delimiter;\n        _alternative = alternative;\n    }\n\n    public override void Evaluate(EvaluationContext ctx, TextWriter output)\n    {\n        var enumerable = _enumerable(ctx);\n        if (enumerable is null or ScalarValue)\n        {\n            _alternative?.Evaluate(ctx, output);\n            return;\n        }\n\n        if (enumerable is SequenceValue sequence)\n        {\n            if (sequence.Elements.Count == 0)\n            {\n                _alternative?.Evaluate(ctx, output);\n                return;\n            }\n\n            for (var i = 0; i < sequence.Elements.Count; ++i)\n            {\n                // Null elements should have been invalid but Serilog didn't check, and so this does occur in the wild.\n                var element = sequence.Elements[i] ?? new ScalarValue(null);\n\n                if (i != 0)\n                {\n                    _delimiter?.Evaluate(ctx, output);\n                }\n\n                var local = _keyOrElementName != null\n                    ? new EvaluationContext(ctx.LogEvent, Locals.Set(ctx.Locals, _keyOrElementName, element))\n                    : ctx;\n\n                local = _valueOrIndexName != null\n                    ? new EvaluationContext(local.LogEvent, Locals.Set(local.Locals, _valueOrIndexName, new ScalarValue(i)))\n                    : local;\n\n                _body.Evaluate(local, output);\n            }\n\n            return;\n        }\n\n        if (enumerable is StructureValue structure)\n        {\n            if (structure.Properties.Count == 0)\n            {\n                _alternative?.Evaluate(ctx, output);\n                return;\n            }\n\n            var first = true;\n            foreach (var member in structure.Properties)\n            {\n                if (first)\n                    first = false;\n                else\n                    _delimiter?.Evaluate(ctx, output);\n\n                var local = _keyOrElementName != null\n                    ? new(ctx.LogEvent, Locals.Set(ctx.Locals, _keyOrElementName, new ScalarValue(member.Name)))\n                    : ctx;\n\n                local = _valueOrIndexName != null\n                    ? new(local.LogEvent, Locals.Set(local.Locals, _valueOrIndexName, member.Value))\n                    : local;\n\n                _body.Evaluate(local, output);\n            }\n        }\n\n        if (enumerable is DictionaryValue dict)\n        {\n            if (dict.Elements.Count == 0)\n            {\n                _alternative?.Evaluate(ctx, output);\n                return;\n            }\n\n            var first = true;\n            foreach (var element in dict.Elements)\n            {\n                if (first)\n                    first = false;\n                else\n                    _delimiter?.Evaluate(ctx, output);\n\n                var local = _keyOrElementName != null\n                    ? new(ctx.LogEvent, Locals.Set(ctx.Locals, _keyOrElementName, element.Key))\n                    : ctx;\n\n                local = _valueOrIndexName != null\n                    ? new(local.LogEvent, Locals.Set(local.Locals, _valueOrIndexName, element.Value))\n                    : local;\n\n                _body.Evaluate(local, output);\n            }\n        }\n\n        // Unsupported; not much we can do.\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Templates/Compilation/CompiledTemplate.cs",
    "content": "// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Expressions;\n\nnamespace Serilog.Templates.Compilation;\n\nabstract class CompiledTemplate\n{\n    public abstract void Evaluate(EvaluationContext ctx, TextWriter output);\n}"
  },
  {
    "path": "src/Serilog.Expressions/Templates/Compilation/CompiledTemplateBlock.cs",
    "content": "// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Expressions;\n\nnamespace Serilog.Templates.Compilation;\n\nclass CompiledTemplateBlock : CompiledTemplate\n{\n    readonly CompiledTemplate[] _elements;\n\n    public CompiledTemplateBlock(CompiledTemplate[] elements)\n    {\n        _elements = elements ?? throw new ArgumentNullException(nameof(elements));\n    }\n\n    public override void Evaluate(EvaluationContext ctx, TextWriter output)\n    {\n        foreach (var element in _elements)\n            element.Evaluate(ctx, output);\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Templates/Compilation/CompiledTimestampToken.cs",
    "content": "// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Expressions;\nusing Serilog.Parsing;\nusing Serilog.Templates.Rendering;\nusing Serilog.Templates.Themes;\n\nnamespace Serilog.Templates.Compilation;\n\nclass CompiledTimestampToken : CompiledTemplate\n{\n    readonly string? _format;\n    readonly Alignment? _alignment;\n    readonly IFormatProvider? _formatProvider;\n    readonly Style _secondaryText;\n\n    public CompiledTimestampToken(string? format, Alignment? alignment, IFormatProvider? formatProvider, TemplateTheme theme)\n    {\n        _format = format;\n        _alignment = alignment;\n        _formatProvider = formatProvider;\n        _secondaryText = theme.GetStyle(TemplateThemeStyle.SecondaryText);\n    }\n\n    public override void Evaluate(EvaluationContext ctx, TextWriter output)\n    {\n        var invisibleCharacterCount = 0;\n\n        if (_alignment == null)\n        {\n            EvaluateUnaligned(ctx, output, _formatProvider, ref invisibleCharacterCount);\n        }\n        else\n        {\n            var writer = new StringWriter();\n            EvaluateUnaligned(ctx, writer, _formatProvider, ref invisibleCharacterCount);\n            Padding.Apply(output, writer.ToString(), _alignment.Value.Widen(invisibleCharacterCount));\n        }\n    }\n\n    void EvaluateUnaligned(EvaluationContext ctx, TextWriter output, IFormatProvider? formatProvider, ref int invisibleCharacterCount)\n    {\n        var value = ctx.LogEvent.Timestamp;\n\n        using var style = _secondaryText.Set(output, ref invisibleCharacterCount);\n\n#if FEATURE_SPAN\n        Span<char> buffer = stackalloc char[36];\n        if (value.TryFormat(buffer, out int charsWritten, _format, _formatProvider))\n        {\n            output.Write(buffer[..charsWritten]);\n            return;\n        }\n#endif\n        output.Write(value.ToString(_format, formatProvider));\n        \n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Templates/Compilation/NameResolution/ExpressionLocalNameBinder.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Expressions.Ast;\nusing Serilog.Expressions.Compilation.Transformations;\n\nnamespace Serilog.Templates.Compilation.NameResolution;\n\nclass ExpressionLocalNameBinder : IdentityTransformer\n{\n    readonly IReadOnlyCollection<string> _localNames;\n\n    public static Expression BindLocalValueNames(Expression expression, IReadOnlyCollection<string> locals)\n    {\n        var expressionLocalNameBinder = new ExpressionLocalNameBinder(locals);\n        return expressionLocalNameBinder.Transform(expression);\n    }\n\n    ExpressionLocalNameBinder(IReadOnlyCollection<string> localNames)\n    {\n        _localNames = localNames ?? throw new ArgumentNullException(nameof(localNames));\n    }\n\n    protected override Expression Transform(AmbientNameExpression px)\n    {\n        if (!px.IsBuiltIn && _localNames.Contains(px.PropertyName))\n            return new LocalNameExpression(px.PropertyName);\n\n        return base.Transform(px);\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Templates/Compilation/NameResolution/TemplateLocalNameBinder.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Templates.Ast;\n\n// ReSharper disable MemberCanBeMadeStatic.Local, SuggestBaseTypeForParameter\n\nnamespace Serilog.Templates.Compilation.NameResolution;\n\nclass TemplateLocalNameBinder\n{\n    public static Template BindLocalValueNames(Template template)\n    {\n        var binder = new TemplateLocalNameBinder();\n        return binder.Transform(template, new());\n    }\n\n    Template Transform(Template template, Stack<string> locals)\n    {\n        return template switch\n        {\n            TemplateBlock block => Transform(block, locals),\n            LiteralText text => text,\n            FormattedExpression fx => Transform(fx, locals),\n            Conditional cond => Transform(cond, locals),\n            Repetition rep => Transform(rep, locals),\n            _ => throw new NotSupportedException(\"Unsupported template type.\")\n        };\n    }\n\n    Template Transform(TemplateBlock block, Stack<string> locals)\n    {\n        return new TemplateBlock(block.Elements\n            .Select(e => Transform(e, locals))\n            .ToArray());\n    }\n\n    Template Transform(FormattedExpression fx, Stack<string> locals)\n    {\n        if (locals.Count == 0)\n            return fx;\n\n        return new FormattedExpression(\n            ExpressionLocalNameBinder.BindLocalValueNames(fx.Expression, locals),\n            fx.Format,\n            fx.Alignment);\n    }\n\n    Template Transform(Conditional cond, Stack<string> locals)\n    {\n        return new Conditional(\n            ExpressionLocalNameBinder.BindLocalValueNames(cond.Condition, locals),\n            Transform(cond.Consequent, locals),\n            cond.Alternative != null ? Transform(cond.Alternative, locals) : null);\n    }\n\n    Template Transform(Repetition rep, Stack<string> locals)\n    {\n        foreach (var name in rep.BindingNames)\n            locals.Push(name);\n\n        var body = Transform(rep.Body, locals);\n\n        foreach (var _ in rep.BindingNames)\n            locals.Pop();\n\n        return new Repetition(\n            ExpressionLocalNameBinder.BindLocalValueNames(rep.Enumerable, locals),\n            rep.BindingNames,\n            body,\n            rep.Delimiter != null ? Transform(rep.Delimiter, locals) : null,\n            rep.Alternative != null ? Transform(rep.Alternative, locals) : null);\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Templates/Compilation/TemplateCompiler.cs",
    "content": "// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Expressions;\nusing Serilog.Expressions.Ast;\nusing Serilog.Expressions.Compilation;\nusing Serilog.Templates.Ast;\nusing Serilog.Templates.Encoding;\nusing Serilog.Templates.Themes;\n\nnamespace Serilog.Templates.Compilation;\n\nstatic class TemplateCompiler\n{\n    public static CompiledTemplate Compile(Template template,\n        IFormatProvider? formatProvider, NameResolver nameResolver,\n        TemplateTheme theme,\n        EncodedTemplateFactory encoder)\n    {\n        return template switch\n        {\n            LiteralText text => new CompiledLiteralText(text.Text, theme),\n            FormattedExpression { Expression: AmbientNameExpression { IsBuiltIn: true, PropertyName: BuiltInProperty.Level} } level =>\n                encoder.Wrap(new CompiledLevelToken(level.Format, level.Alignment, theme)),\n            FormattedExpression\n            {\n                Expression: AmbientNameExpression { IsBuiltIn: true, PropertyName: BuiltInProperty.Exception },\n                Alignment: null,\n                Format: null\n            } => encoder.Wrap(new CompiledExceptionToken(theme)),\n            FormattedExpression\n            {\n                Expression: AmbientNameExpression { IsBuiltIn: true, PropertyName: BuiltInProperty.Message },\n                Format: null\n            } message => encoder.Wrap(new CompiledMessageToken(formatProvider, message.Alignment, theme)),\n            FormattedExpression\n            {\n                Expression: AmbientNameExpression { IsBuiltIn: true, PropertyName: BuiltInProperty.Timestamp },\n            } timestamp => encoder.Wrap(new CompiledTimestampToken(timestamp.Format, timestamp.Alignment, formatProvider, theme)),\n            FormattedExpression expression => encoder.MakeCompiledFormattedExpression(\n                ExpressionCompiler.Compile(expression.Expression, formatProvider, nameResolver), expression.Format, expression.Alignment, formatProvider, theme),\n            TemplateBlock block => new CompiledTemplateBlock(block.Elements.Select(e => Compile(e, formatProvider, nameResolver, theme, encoder)).ToArray()),\n            Conditional conditional => new CompiledConditional(\n                ExpressionCompiler.Compile(conditional.Condition, formatProvider, nameResolver),\n                Compile(conditional.Consequent, formatProvider, nameResolver, theme, encoder),\n                conditional.Alternative == null ? null : Compile(conditional.Alternative, formatProvider, nameResolver, theme, encoder)),\n            Repetition repetition => new CompiledRepetition(\n                ExpressionCompiler.Compile(repetition.Enumerable, formatProvider, nameResolver),\n                repetition.BindingNames.Length > 0 ? repetition.BindingNames[0] : null,\n                repetition.BindingNames.Length > 1 ? repetition.BindingNames[1] : null,\n                Compile(repetition.Body, formatProvider, nameResolver, theme, encoder),\n                repetition.Delimiter == null ? null : Compile(repetition.Delimiter, formatProvider, nameResolver, theme, encoder),\n                repetition.Alternative == null ? null : Compile(repetition.Alternative, formatProvider, nameResolver, theme, encoder)),\n            _ => throw new NotSupportedException()\n        };\n    }\n}\n"
  },
  {
    "path": "src/Serilog.Expressions/Templates/Compilation/TemplateFunctionNameResolver.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Expressions;\nusing Serilog.Expressions.Compilation;\nusing Serilog.Expressions.Runtime;\nusing Serilog.Templates.Ast;\nusing Serilog.Templates.Compilation.UnreferencedProperties;\nusing Serilog.Templates.Compilation.Unsafe;\n\nnamespace Serilog.Templates.Compilation;\n\nstatic class TemplateFunctionNameResolver\n{\n    public static NameResolver Build(NameResolver? additionalNameResolver, Template template)\n    {\n        var resolvers = new List<NameResolver>\n        {\n            new StaticMemberNameResolver(typeof(RuntimeOperators)),\n            new UnreferencedPropertiesFunction(template),\n            new UnsafeOutputFunction()\n        };\n\n        if (additionalNameResolver != null)\n            resolvers.Add(additionalNameResolver);\n\n        return new OrderedNameResolver(resolvers);\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Templates/Compilation/UnreferencedProperties/ExpressionReferencedPropertiesFinder.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Expressions;\nusing Serilog.Expressions.Ast;\nusing Serilog.Expressions.Compilation;\nusing Serilog.Expressions.Compilation.Transformations;\n\nnamespace Serilog.Templates.Compilation.UnreferencedProperties;\n\nclass ExpressionReferencedPropertiesFinder : SerilogExpressionTransformer<IEnumerable<string>>\n{\n    public IEnumerable<string> FindReferencedProperties(Expression expression)\n    {\n        return Transform(expression);\n    }\n\n    protected override IEnumerable<string> Transform(CallExpression call)\n    {\n        return call.Operands.SelectMany(Transform);\n    }\n\n    protected override IEnumerable<string> Transform(ConstantExpression cx)\n    {\n        yield break;\n    }\n\n    protected override IEnumerable<string> Transform(AmbientNameExpression px)\n    {\n        if (!px.IsBuiltIn)\n            yield return px.PropertyName;\n    }\n\n    protected override IEnumerable<string> Transform(LocalNameExpression nlx)\n    {\n        yield break;\n    }\n\n    protected override IEnumerable<string> Transform(AccessorExpression spx)\n    {\n        if (Pattern.IsAmbientProperty(spx.Receiver, BuiltInProperty.Properties, true))\n            yield return spx.MemberName;\n        \n        foreach (var nested in Transform(spx.Receiver))\n            yield return nested;\n    }\n\n    protected override IEnumerable<string> Transform(LambdaExpression lmx)\n    {\n        return Transform(lmx.Body);\n    }\n\n    protected override IEnumerable<string> Transform(ParameterExpression prx)\n    {\n        yield break;\n    }\n\n    protected override IEnumerable<string> Transform(IndexerWildcardExpression wx)\n    {\n        yield break;\n    }\n\n    protected override IEnumerable<string> Transform(ArrayExpression ax)\n    {\n        return ax.Elements.OfType<ItemElement>().SelectMany(i => Transform(i.Value))\n            .Concat(ax.Elements.OfType<SpreadElement>().SelectMany(i => Transform(i.Content)));\n    }\n\n    protected override IEnumerable<string> Transform(ObjectExpression ox)\n    {\n        return ox.Members.OfType<PropertyMember>().SelectMany(m => Transform(m.Value))\n            .Concat(ox.Members.OfType<SpreadMember>().SelectMany(m => Transform(m.Content)));\n    }\n\n    protected override IEnumerable<string> Transform(IndexerExpression ix)\n    {\n        if (Pattern.IsAmbientProperty(ix.Receiver, BuiltInProperty.Properties, true) &&\n            Pattern.IsStringConstant(ix.Index, out var name))\n        {\n            yield return name;\n        }\n        else\n        {\n            foreach (var nested in Transform(ix.Index).Concat(Transform(ix.Receiver)))\n            {\n                yield return nested;\n            }\n        }\n    }\n\n    protected override IEnumerable<string> Transform(IndexOfMatchExpression mx)\n    {\n        return Transform(mx.Corpus);\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Templates/Compilation/UnreferencedProperties/TemplateReferencedPropertiesFinder.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Templates.Ast;\n\nnamespace Serilog.Templates.Compilation.UnreferencedProperties;\n\nclass TemplateReferencedPropertiesFinder\n{\n    readonly ExpressionReferencedPropertiesFinder _rpf = new();\n\n    public IEnumerable<string> FindReferencedProperties(Template template)\n    {\n        return template switch\n        {\n            Conditional conditional => _rpf.FindReferencedProperties(conditional.Condition)\n                .Concat(FindReferencedProperties(conditional.Consequent))\n                .Concat(conditional.Alternative != null\n                    ? FindReferencedProperties(conditional.Alternative)\n                    : Enumerable.Empty<string>()),\n            FormattedExpression formattedExpression =>\n                _rpf.FindReferencedProperties(formattedExpression.Expression),\n            LiteralText => Enumerable.Empty<string>(),\n            Repetition repetition => _rpf.FindReferencedProperties(repetition.Enumerable)\n                .Concat(FindReferencedProperties(repetition.Body))\n                .Concat(repetition.Alternative != null\n                    ? FindReferencedProperties(repetition.Alternative)\n                    : Enumerable.Empty<string>())\n                .Concat(repetition.Delimiter != null\n                    ? FindReferencedProperties(repetition.Delimiter)\n                    : Enumerable.Empty<string>()),\n            TemplateBlock templateBlock => templateBlock.Elements.SelectMany(FindReferencedProperties),\n            _ => throw new ArgumentOutOfRangeException(nameof(template))\n        };\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Templates/Compilation/UnreferencedProperties/UnreferencedPropertiesFunction.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing System.Diagnostics.CodeAnalysis;\nusing System.Reflection;\nusing Serilog.Events;\nusing Serilog.Expressions;\nusing Serilog.Expressions.Runtime;\nusing Serilog.Parsing;\nusing Serilog.Templates.Ast;\n\nnamespace Serilog.Templates.Compilation.UnreferencedProperties;\n\n/// <summary>\n/// This little extension implements the <c>rest()</c> function in expression templates. It's based on\n/// <c>Serilog.Sinks.SystemConsole.PropertiesTokenRenderer</c>, and is equivalent to how <c>Properties</c> is rendered by\n/// the console sink. <c>rest()</c> will return a structure containing all of the user-defined properties from a\n/// log event except those referenced in either the event's message template, or the expression template itself.\n/// </summary>\n/// <remarks>\n/// The existing semantics of <c>Properties</c> in output templates isn't suitable for expression templates. The\n/// <c>@p</c> object provides access to <em>all</em> event properties in an expression template, so it would make no\n/// sense to render that object without all of its members.\n/// </remarks>\nclass UnreferencedPropertiesFunction : NameResolver\n{\n    const string FunctionName = \"rest\";\n\n    readonly HashSet<string> _referencedInTemplate;\n\n    public UnreferencedPropertiesFunction(Template template)\n    {\n        var finder = new TemplateReferencedPropertiesFinder();\n        _referencedInTemplate = [..finder.FindReferencedProperties(template)];\n    }\n\n    public override bool TryBindFunctionParameter(ParameterInfo parameter, [MaybeNullWhen(false)] out object boundValue)\n    {\n        if (parameter.ParameterType == typeof(UnreferencedPropertiesFunction))\n        {\n            boundValue = this;\n            return true;\n        }\n\n        boundValue = null;\n        return false;\n    }\n\n    public override bool TryResolveFunctionName(string name, [MaybeNullWhen(false)] out MethodInfo implementation)\n    {\n        if (name.Equals(FunctionName, StringComparison.OrdinalIgnoreCase))\n        {\n            implementation = typeof(UnreferencedPropertiesFunction).GetMethod(nameof(Implementation),\n                BindingFlags.Static | BindingFlags.Public)!;\n            return true;\n        }\n\n        implementation = null;\n        return false;\n    }\n\n    // By convention, built-in functions accept and return nullable values.\n    // ReSharper disable once ReturnTypeCanBeNotNullable\n    public static LogEventPropertyValue? Implementation(UnreferencedPropertiesFunction self, LogEvent logEvent, LogEventPropertyValue? deep = null)\n    {\n        var checkMessageTemplate = Coerce.IsTrue(deep);\n        return new StructureValue(logEvent.Properties\n            .Where(kvp => !self._referencedInTemplate.Contains(kvp.Key) &&\n                          (!checkMessageTemplate || !TemplateContainsPropertyName(logEvent.MessageTemplate, kvp.Key)))\n            .Select(kvp => new LogEventProperty(kvp.Key, kvp.Value)));\n    }\n\n    static bool TemplateContainsPropertyName(MessageTemplate messageTemplate, string propertyName)\n    {\n        foreach (var token in messageTemplate.Tokens)\n        {\n            if (token is PropertyToken namedProperty &&\n                namedProperty.PropertyName == propertyName)\n            {\n                return true;\n            }\n        }\n\n        return false;\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Templates/Compilation/Unsafe/UnsafeOutputFunction.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing System.Diagnostics.CodeAnalysis;\nusing System.Reflection;\nusing Serilog.Events;\nusing Serilog.Expressions;\nusing Serilog.Templates.Encoding;\n\nnamespace Serilog.Templates.Compilation.Unsafe;\n\n/// <summary>\n/// Marks an expression in a template as bypassing the output encoding mechanism.\n/// </summary>\nclass UnsafeOutputFunction : NameResolver\n{\n    const string FunctionName = \"unsafe\";\n\n    public override bool TryResolveFunctionName(string name, [MaybeNullWhen(false)] out MethodInfo implementation)\n    {\n        if (name.Equals(FunctionName, StringComparison.OrdinalIgnoreCase))\n        {\n            implementation = typeof(UnsafeOutputFunction).GetMethod(nameof(Implementation),\n                BindingFlags.Static | BindingFlags.Public)!;\n            return true;\n        }\n\n        implementation = null;\n        return false;\n    }\n\n    // By convention, built-in functions accept and return nullable values.\n    // ReSharper disable once ReturnTypeCanBeNotNullable\n    public static LogEventPropertyValue? Implementation(LogEventPropertyValue? inner)\n    {\n        return new ScalarValue(new PreEncodedValue(inner));\n    }\n}\n"
  },
  {
    "path": "src/Serilog.Expressions/Templates/Encoding/EncodedCompiledTemplate.cs",
    "content": "﻿using Serilog.Expressions;\nusing Serilog.Templates.Compilation;\n\nnamespace Serilog.Templates.Encoding\n{\n    class EncodedCompiledTemplate : CompiledTemplate\n    {\n        readonly CompiledTemplate _inner;\n        readonly TemplateOutputEncoder _encoder;\n\n        public EncodedCompiledTemplate(CompiledTemplate inner, TemplateOutputEncoder encoder)\n        {\n            _inner = inner;\n            _encoder = encoder;\n        }\n\n        public override void Evaluate(EvaluationContext ctx, TextWriter output)\n        {\n            var buffer = new StringWriter(output.FormatProvider);\n            _inner.Evaluate(ctx, buffer);\n            var encoded = _encoder.Encode(buffer.ToString());\n            output.Write(encoded);\n        }\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Templates/Encoding/EncodedTemplateFactory.cs",
    "content": "﻿using Serilog.Expressions;\nusing Serilog.Parsing;\nusing Serilog.Templates.Compilation;\nusing Serilog.Templates.Themes;\n\nnamespace Serilog.Templates.Encoding\n{\n    class EncodedTemplateFactory\n    {\n        readonly TemplateOutputEncoder? _encoder;\n\n        public EncodedTemplateFactory(TemplateOutputEncoder? encoder)\n        {\n            _encoder = encoder;\n        }\n        \n        public CompiledTemplate Wrap(CompiledTemplate inner)\n        {\n            if (_encoder == null)\n                return inner;\n\n            return new EncodedCompiledTemplate(inner, _encoder);\n        }\n        \n        public CompiledTemplate MakeCompiledFormattedExpression(Evaluatable expression, string? format, Alignment? alignment, IFormatProvider? formatProvider, TemplateTheme theme)\n        {\n            if (_encoder == null)\n                return new CompiledFormattedExpression(expression, format, alignment, formatProvider, theme);\n            \n            return new EscapableEncodedCompiledFormattedExpression(expression, format, alignment, formatProvider, theme, _encoder);\n        }\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Templates/Encoding/EscapableEncodedCompiledFormattedExpression.cs",
    "content": "﻿using Serilog.Events;\nusing Serilog.Expressions;\nusing Serilog.Expressions.Runtime;\nusing Serilog.Parsing;\nusing Serilog.Templates.Compilation;\nusing Serilog.Templates.Themes;\n\nnamespace Serilog.Templates.Encoding\n{\n    class EscapableEncodedCompiledFormattedExpression : CompiledTemplate\n    {\n        static int _nextSubstituteLocalNameSuffix;\n        readonly string _substituteLocalName = $\"%sub{Interlocked.Increment(ref _nextSubstituteLocalNameSuffix)}\";\n        readonly Evaluatable _expression;\n        readonly TemplateOutputEncoder _encoder;\n        readonly CompiledFormattedExpression _inner;\n\n        public EscapableEncodedCompiledFormattedExpression(Evaluatable expression, string? format, Alignment? alignment, IFormatProvider? formatProvider, TemplateTheme theme, TemplateOutputEncoder encoder)\n        {\n            _expression = expression;\n            _encoder = encoder;\n            \n            // `expression` can't be passed through, because it may include calls to the `unsafe()` function (nested in arbitrary subexpressions) that\n            // need to be evaluated first. So, instead, we evaluate `expression` and unwrap the result of `unsafe`, placing the result in a local variable\n            // that the formatting expression we construct here can read from.\n            _inner = new CompiledFormattedExpression(GetSubstituteLocalValue, format, alignment, formatProvider, theme);\n        }\n\n        LogEventPropertyValue? GetSubstituteLocalValue(EvaluationContext context)\n        {\n            return Locals.TryGetValue(context.Locals, _substituteLocalName, out var computed)\n                ? computed\n                : null;\n        }\n\n        public override void Evaluate(EvaluationContext ctx, TextWriter output)\n        {\n            var value = _expression(ctx);\n            \n            if (value is ScalarValue { Value: PreEncodedValue pv })\n            {\n                var rawContext = pv.Inner == null ?\n                    new EvaluationContext(ctx.LogEvent) :\n                    new EvaluationContext(ctx.LogEvent, Locals.Set(ctx.Locals, _substituteLocalName, pv.Inner));\n                _inner.Evaluate(rawContext, output);\n                return;\n            }\n\n            var buffer = new StringWriter(output.FormatProvider);\n            \n            var bufferedContext = value == null\n                ? ctx\n                : new EvaluationContext(ctx.LogEvent, Locals.Set(ctx.Locals, _substituteLocalName, value));\n\n            _inner.Evaluate(bufferedContext, buffer);\n            var encoded = _encoder.Encode(buffer.ToString());\n            output.Write(encoded);\n        }\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Templates/Encoding/PreEncodedValue.cs",
    "content": "﻿using Serilog.Events;\n\nnamespace Serilog.Templates.Encoding\n{\n    class PreEncodedValue\n    {\n        public LogEventPropertyValue? Inner { get; }\n\n        public PreEncodedValue(LogEventPropertyValue? inner)\n        {\n            Inner = inner;\n        }\n\n        public override string ToString()\n        {\n            // This code path indicates that the template expects encoding to be performed, but no encoder is\n            // registered (probably a bad situation to be in).\n            throw new InvalidOperationException(\"No output encoder is registered.\");\n        }\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Templates/Encoding/TemplateOutputEncoder.cs",
    "content": "﻿namespace Serilog.Templates.Encoding\n{\n    /// <summary>\n    /// An encoder applied to the output substituted into template holes.\n    /// </summary>\n    public abstract class TemplateOutputEncoder\n    {\n        /// <summary>\n        /// Encode <paramref name=\"value\" />.\n        /// </summary>\n        /// <param name=\"value\">The raw template output to encode.</param>\n        /// <returns>The encoded output.</returns>\n        public abstract string Encode(string value);\n    }\n}\n"
  },
  {
    "path": "src/Serilog.Expressions/Templates/ExpressionTemplate.cs",
    "content": "// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing System.Diagnostics.CodeAnalysis;\nusing Serilog.Events;\nusing Serilog.Expressions;\nusing Serilog.Formatting;\nusing Serilog.Templates.Compilation;\nusing Serilog.Templates.Compilation.NameResolution;\nusing Serilog.Templates.Encoding;\nusing Serilog.Templates.Parsing;\nusing Serilog.Templates.Themes;\n\nnamespace Serilog.Templates;\n\n/// <summary>\n/// Formats <see cref=\"LogEvent\"/>s into text using embedded expressions.\n/// </summary>\npublic class ExpressionTemplate : ITextFormatter\n{\n    readonly CompiledTemplate _compiled;\n\n    /// <summary>\n    /// Construct an <see cref=\"ExpressionTemplate\"/>.\n    /// </summary>\n    /// <param name=\"template\">The template text.</param>\n    /// <param name=\"result\">The parsed template, if successful.</param>\n    /// <param name=\"error\">A description of the error, if unsuccessful.</param>\n    /// <returns><c langword=\"true\">true</c> if the template was well-formed.</returns>\n    public static bool TryParse(\n        string template,\n        [MaybeNullWhen(false)] out ExpressionTemplate result,\n        [MaybeNullWhen(true)] out string error)\n    {\n        if (template == null) throw new ArgumentNullException(nameof(template));\n        return TryParse(template, null, null, null, false, null, out result, out error);\n    }\n\n    /// <summary>\n    /// Construct an <see cref=\"ExpressionTemplate\"/>.\n    /// </summary>\n    /// <param name=\"template\">The template text.</param>\n    /// <param name=\"formatProvider\">Optionally, an <see cref=\"IFormatProvider\"/> to use when formatting\n    /// embedded values.</param>\n    /// <param name=\"theme\">Optionally, an ANSI theme to apply to the template output.</param>\n    /// <param name=\"result\">The parsed template, if successful.</param>\n    /// <param name=\"error\">A description of the error, if unsuccessful.</param>\n    /// <param name=\"nameResolver\">Optionally, a <see cref=\"NameResolver\"/>\n    /// with which to resolve function names that appear in the template.</param>\n    /// <param name=\"applyThemeWhenOutputIsRedirected\">Apply <paramref name=\"theme\"/> even when\n    /// <see cref=\"System.Console.IsOutputRedirected\"/> or <see cref=\"Console.IsErrorRedirected\"/> returns <c>true</c>.</param>\n    /// <param name=\"encoder\">Optionally, an encoder to apply to output substituted into template holes.</param>\n    /// <returns><c langword=\"true\">true</c> if the template was well-formed.</returns>\n    public static bool TryParse(\n        string template,\n        IFormatProvider? formatProvider,\n        NameResolver? nameResolver,\n        TemplateTheme? theme,\n        bool applyThemeWhenOutputIsRedirected,\n        TemplateOutputEncoder? encoder,\n        [MaybeNullWhen(false)] out ExpressionTemplate result,\n        [MaybeNullWhen(true)] out string error)\n    {\n        if (template == null) throw new ArgumentNullException(nameof(template));\n\n        var templateParser = new TemplateParser();\n        if (!templateParser.TryParse(template, out var parsed, out error))\n        {\n            result = null;\n            return false;\n        }\n\n        var planned = TemplateLocalNameBinder.BindLocalValueNames(parsed);\n\n        result = new(\n            TemplateCompiler.Compile(\n                planned,\n                formatProvider,\n                TemplateFunctionNameResolver.Build(nameResolver, planned),\n                SelectTheme(theme, applyThemeWhenOutputIsRedirected),\n                new EncodedTemplateFactory(encoder)));\n\n        return true;\n    }\n\n    ExpressionTemplate(CompiledTemplate compiled)\n    {\n        _compiled = compiled;\n    }\n\n    /// <summary>\n    /// Construct an <see cref=\"ExpressionTemplate\"/>.\n    /// </summary>\n    /// <param name=\"template\">The template text.</param>\n    /// <param name=\"formatProvider\">Optionally, an <see cref=\"IFormatProvider\"/> to use when formatting\n    /// embedded values.</param>\n    /// <param name=\"nameResolver\">Optionally, a <see cref=\"NameResolver\"/>\n    /// with which to resolve function names that appear in the template.</param>\n    /// <param name=\"theme\">Optionally, an ANSI theme to apply to the template output.</param>\n    /// <param name=\"applyThemeWhenOutputIsRedirected\">Apply <paramref name=\"theme\"/> even when\n    /// <see cref=\"Console.IsOutputRedirected\"/> or <see cref=\"Console.IsErrorRedirected\"/> returns <c>true</c>.</param>\n    /// <param name=\"encoder\">Optionally, an encoder to apply to output substituted into template holes.</param>\n    public ExpressionTemplate(\n        string template,\n        IFormatProvider? formatProvider = null,\n        NameResolver? nameResolver = null,\n        TemplateTheme? theme = null,\n        bool applyThemeWhenOutputIsRedirected = false,\n        TemplateOutputEncoder? encoder = null)\n    {\n        if (template == null) throw new ArgumentNullException(nameof(template));\n\n        var templateParser = new TemplateParser();\n        if (!templateParser.TryParse(template, out var parsed, out var error))\n            throw new ArgumentException(error);\n\n        var planned = TemplateLocalNameBinder.BindLocalValueNames(parsed);\n\n        _compiled = TemplateCompiler.Compile(\n            planned,\n            formatProvider,\n            TemplateFunctionNameResolver.Build(nameResolver, planned),\n            SelectTheme(theme, applyThemeWhenOutputIsRedirected),\n            new EncodedTemplateFactory(encoder));\n    }\n\n    static TemplateTheme SelectTheme(TemplateTheme? supplied, bool applyThemeWhenOutputIsRedirected)\n    {\n        if (supplied == null ||\n            (Console.IsOutputRedirected || Console.IsErrorRedirected) && !applyThemeWhenOutputIsRedirected)\n        {\n            return TemplateTheme.None;\n        }\n\n        return supplied;\n    }\n\n    /// <inheritdoc />\n    public void Format(LogEvent logEvent, TextWriter output)\n    {\n        _compiled.Evaluate(new(logEvent), output);\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Templates/Parsing/TemplateParser.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing System.Diagnostics.CodeAnalysis;\nusing Serilog.Templates.Ast;\n\nnamespace Serilog.Templates.Parsing;\n\nclass TemplateParser\n{\n    readonly TemplateTokenizer _tokenizer = new();\n    readonly TemplateTokenParsers _templateTokenParsers = new();\n\n    public bool TryParse(\n        string template,\n        [MaybeNullWhen(false)] out Template parsed,\n        [MaybeNullWhen(true)] out string error)\n    {\n        if (template == null) throw new ArgumentNullException(nameof(template));\n\n        var tokenList = _tokenizer.TryTokenize(template);\n        if (!tokenList.HasValue)\n        {\n            error = tokenList.ToString();\n            parsed = null;\n            return false;\n        }\n\n        var result = _templateTokenParsers.TryParse(tokenList.Value);\n        if (!result.HasValue)\n        {\n            error = result.ToString();\n            parsed = null;\n            return false;\n        }\n\n        parsed = result.Value;\n        error = null;\n        return true;\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Templates/Parsing/TemplateTokenParsers.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Expressions.Ast;\nusing Serilog.Expressions.Parsing;\nusing Serilog.Parsing;\nusing Serilog.ParserConstruction;\nusing Serilog.ParserConstruction.Model;\nusing Serilog.ParserConstruction.Parsers;\nusing Serilog.Templates.Ast;\nusing static Serilog.Expressions.Parsing.ExpressionToken;\n\n// ReSharper disable SuggestBaseTypeForParameter, ConvertIfStatementToSwitchStatement, AccessToModifiedClosure\n\nnamespace Serilog.Templates.Parsing;\n\nclass TemplateTokenParsers\n{\n    readonly TokenListParser<ExpressionToken, Template> _template;\n\n    public TemplateTokenParsers()\n    {\n        TokenListParser<ExpressionToken, Template>? block = null;\n\n        var alignment =\n            Token.EqualTo(Comma).IgnoreThen(\n                (from direction in Token.EqualTo(Minus).Value(AlignmentDirection.Left)\n                        .OptionalOrDefault(AlignmentDirection.Right)\n                    from width in Token.EqualTo(Number).Apply(Numerics.NaturalUInt32)\n                    select new Alignment(direction, (int) width)).Named(\"alignment and width\"));\n\n        var format = Token.EqualTo(Colon)\n            .IgnoreThen(Token.EqualTo(Format))\n            .Select(fmt => (string?) fmt.ToStringValue());\n\n        var hole =\n            from _ in Token.EqualTo(LBrace)\n            from expr in ExpressionTokenParsers.Expr\n            from align in alignment.Select(a => (Alignment?)a).OptionalOrDefault()\n            from fmt in format.OptionalOrDefault()\n            from __ in Token.EqualTo(RBrace)\n            select (Template) new FormattedExpression(expr, fmt, align);\n\n        static TokenListParser<ExpressionToken, Expression?> Directive(\n            bool hasArgument,\n            params ExpressionToken[] signifiers)\n        {\n            var open = Token.EqualTo(LBraceHash)\n                .IgnoreThen(Token.Sequence(signifiers)).Try();\n\n            if (hasArgument)\n                return open\n                    .IgnoreThen(ExpressionTokenParsers.Expr.Cast<ExpressionToken, Expression, Expression?>())\n                    .Then(v => Token.EqualTo(RBrace).Value(v));\n\n            return open.IgnoreThen(Token.EqualTo(RBrace)).Value((Expression?) null);\n        }\n\n        static Template? LeftReduceConditional((Expression?, Template)[] first, Template? last)\n        {\n            for (var i = first.Length - 1; i >= 0; i--)\n            {\n                last = new Conditional(first[i].Item1!, first[i].Item2, last);\n            }\n\n            return last;\n        }\n\n        var conditional =\n            from iff in Directive(true, If)\n            from consequent in Parse.Ref(() => block!)\n            from alternatives in Directive(true, Else, If)\n                .Then(elsif => Parse.Ref(() => block!).Select(b => (elsif, b)))\n                .Many()\n            from final in Directive(false, Else)\n                .IgnoreThen(Parse.Ref(() => block!).Select(b => ((Expression?) null, b)))\n                .OptionalOrDefault()\n            from end in Directive(false, End)\n            let firstAlt = LeftReduceConditional(alternatives, final.b)\n            select (Template) new Conditional(iff!, consequent, firstAlt);\n\n        var eachDirective =\n            Token.EqualTo(LBraceHash)\n                .IgnoreThen(Token.EqualTo(Each)).Try()\n                .IgnoreThen(Token.EqualTo(Identifier)\n                    .Select(i => i.ToStringValue())\n                    .AtLeastOnceDelimitedBy(Token.EqualTo(Comma)))\n                .Then(bindings => Token.EqualTo(In).Value(bindings))\n                .Then(bindings => ExpressionTokenParsers.Expr.Cast<ExpressionToken, Expression, Expression?>()\n                    .Select(enumerable => new {enumerable, bindings}))\n                .Then(v => Token.EqualTo(RBrace).Value(v));\n\n        var repetition =\n            from each in eachDirective\n            from body in Parse.Ref(() => block!)\n            from delimiter in Directive(false, Delimit)\n                .IgnoreThen(Parse.Ref(() => block!))\n                .Cast<ExpressionToken, Template, Template?>()\n                .OptionalOrDefault()\n            from alternative in Directive(false, Else)\n                .IgnoreThen(Parse.Ref(() => block!))\n                .Cast<ExpressionToken, Template, Template?>()\n                .OptionalOrDefault()\n            from end in Directive(false, End)\n            select (Template) new Repetition(\n                each.enumerable,\n                each.bindings,\n                body,\n                delimiter,\n                alternative);\n\n        var element = Token.EqualTo(Text).Select(t => (Template)new LiteralText(t.ToStringValue()))\n            .Or(Token.EqualTo(DoubleLBrace)\n                .Value((Template) new LiteralText(\"{\")))\n            .Or(Token.EqualTo(DoubleRBrace)\n                .Value((Template) new LiteralText(\"}\")))\n            .Or(conditional)\n            .Or(repetition)\n            .Or(hole);\n\n        block = element.Many().Select(elements => elements.Length == 1 ?\n            elements[0] :\n            new TemplateBlock(elements));\n\n        _template = block.AtEnd();\n    }\n\n    public TokenListParserResult<ExpressionToken, Template> TryParse(\n        TokenList<ExpressionToken> input)\n    {\n        return _template.TryParse(input);\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Templates/Parsing/TemplateTokenizer.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Expressions.Parsing;\nusing Serilog.ParserConstruction;\nusing Serilog.ParserConstruction.Model;\n\nnamespace Serilog.Templates.Parsing;\n\nclass TemplateTokenizer : Tokenizer<ExpressionToken>\n{\n    readonly ExpressionTokenizer _expressionTokenizer = new();\n\n    protected override IEnumerable<Result<ExpressionToken>> Tokenize(TextSpan span)\n    {\n        var start = span;\n        var rem = start;\n        do\n        {\n            var next = rem.ConsumeChar();\n            if (!next.HasValue)\n            {\n                if (rem != start)\n                    yield return Result.Value(ExpressionToken.Text, start, rem);\n\n                yield break;\n            }\n\n            if (next.Value == '{')\n            {\n                if (rem != start)\n                    yield return Result.Value(ExpressionToken.Text, start, rem);\n\n                var peek = next.Remainder.ConsumeChar();\n                if (peek.HasValue && peek.Value == '{')\n                {\n                    yield return Result.Value(ExpressionToken.DoubleLBrace, next.Location, peek.Remainder);\n                    start = rem = peek.Remainder;\n                }\n                else\n                {\n                    if (peek.HasValue && peek.Value == '#')\n                    {\n                        yield return Result.Value(ExpressionToken.LBraceHash, next.Location, peek.Remainder);\n                        start = rem = peek.Remainder;\n                    }\n                    else\n                    {\n                        yield return Result.Value(ExpressionToken.LBrace, next.Location, next.Remainder);\n                        start = rem = next.Remainder;\n                    }\n\n                    foreach (var token in TokenizeHole(rem))\n                    {\n                        yield return token;\n                        start = rem = token.Remainder;\n                    }\n                }\n            }\n            else if (next.Value == '}')\n            {\n                if (rem != start)\n                    yield return Result.Value(ExpressionToken.Text, start, rem);\n\n                var peek = next.Remainder.ConsumeChar();\n                if (peek.HasValue && peek.Value == '}')\n                {\n                    yield return Result.Value(ExpressionToken.DoubleRBrace, next.Location, peek.Remainder);\n                    start = rem = peek.Remainder;\n                }\n                else\n                {\n                    yield return Result.Empty<ExpressionToken>(next.Remainder, [\"escaped `}`\"]);\n                    yield break;\n                }\n            }\n            else\n            {\n                rem = next.Remainder;\n            }\n        } while (true);\n    }\n\n    IEnumerable<Result<ExpressionToken>> TokenizeHole(TextSpan span)\n    {\n        // Stack braces, brackets, and parens.\n        // If we hit , or :, the stack is empty, and everything we've seen is balanced, we switch into\n        // alignment/width tokenization.\n        // If we hit } and the stack is empty, and everything we've seen is balanced, we yield the final\n        // '}' and return to literal text mode.\n\n        var toMatch = new Stack<ExpressionToken>();\n        var unbalanced = false;\n\n        foreach (var token in _expressionTokenizer.LazyTokenize(span))\n        {\n            if (unbalanced)\n                yield break;\n\n            yield return token;\n\n            if (!token.HasValue)\n                yield break;\n\n            if (token.Value is\n                ExpressionToken.LParen or\n                ExpressionToken.LBrace or\n                ExpressionToken.LBracket)\n            {\n                toMatch.Push(token.Value);\n            }\n            else if (toMatch.Count > 0)\n            {\n                if (token.Value == ExpressionToken.RParen)\n                {\n                    if (toMatch.Peek() != ExpressionToken.LParen)\n                        unbalanced = true;\n                    else\n                        toMatch.Pop();\n                }\n                else if (token.Value == ExpressionToken.RBrace)\n                {\n                    if (toMatch.Peek() != ExpressionToken.LBrace)\n                        unbalanced = true;\n                    else\n                        toMatch.Pop();\n                }\n                else if (token.Value == ExpressionToken.RBracket)\n                {\n                    if (toMatch.Peek() != ExpressionToken.LBracket)\n                        unbalanced = true;\n                    else\n                        toMatch.Pop();\n                }\n            }\n            else if (token.Value == ExpressionToken.RBrace)\n            {\n                yield break;\n            }\n            // The default tokenization of `,` alignment (comma, [minus], number) is fine, only\n            // formats require special handling.\n            else if (token.Value == ExpressionToken.Colon)\n            {\n                var formatStart = token.Remainder;\n                var next = formatStart.ConsumeChar();\n                while (next.HasValue)\n                {\n                    if (next.Value == '}')\n                    {\n                        yield return Result.Value(ExpressionToken.Format, formatStart, next.Location);\n                        yield return Result.Value(ExpressionToken.RBrace, next.Location, next.Remainder);\n                        yield break;\n                    }\n                    next = next.Remainder.ConsumeChar();\n                }\n\n                if (formatStart != next.Location)\n                {\n                    yield return Result.Value(ExpressionToken.Format, formatStart, next.Location);\n                }\n\n                yield break;\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Templates/Rendering/AlignmentExtensions.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Parsing;\n\nnamespace Serilog.Templates.Rendering;\n\nstatic class AlignmentExtensions\n{\n    public static Alignment Widen(this Alignment alignment, int amount)\n    {\n        return new(alignment.Direction, alignment.Width + amount);\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Templates/Rendering/Casing.cs",
    "content": "﻿// Copyright Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nnamespace Serilog.Templates.Rendering;\n\nstatic class Casing\n{\n    /// <summary>\n    /// Apply upper or lower casing to <paramref name=\"value\"/> when <paramref name=\"format\"/> is provided.\n    /// Returns <paramref name=\"value\"/> when no or invalid format provided.\n    /// </summary>\n    /// <param name=\"value\">Provided string for formatting.</param>\n    /// <param name=\"format\">Format string.</param>\n    /// <returns>The provided <paramref name=\"value\"/> with formatting applied.</returns>\n    public static string Format(string value, string? format = null)\n    {\n        switch (format)\n        {\n            case \"u\":\n                return value.ToUpperInvariant();\n            case \"w\":\n                return value.ToLowerInvariant();\n            default:\n                return value;\n        }\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Templates/Rendering/LevelRenderer.cs",
    "content": "﻿// Copyright 2017 Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Events;\n\n// ReSharper disable StringLiteralTypo\n\nnamespace Serilog.Templates.Rendering;\n\n/// <summary>\n/// Implements the {Level} element.\n/// can now have a fixed width applied to it, as well as casing rules.\n/// Width is set through formats like \"u3\" (uppercase three chars),\n/// \"w1\" (one lowercase char), or \"t4\" (title case four chars).\n/// </summary>\nstatic class LevelRenderer\n{\n    static readonly string[][] TitleCaseLevelMap =\n    [\n        [\"V\", \"Vb\", \"Vrb\", \"Verb\"],\n        [\"D\", \"De\", \"Dbg\", \"Dbug\"],\n        [\"I\", \"In\", \"Inf\", \"Info\"],\n        [\"W\", \"Wn\", \"Wrn\", \"Warn\"],\n        [\"E\", \"Er\", \"Err\", \"Eror\"],\n        [\"F\", \"Fa\", \"Ftl\", \"Fatl\"]\n    ];\n\n    static readonly string[][] LowercaseLevelMap =\n    [\n        [\"v\", \"vb\", \"vrb\", \"verb\"],\n        [\"d\", \"de\", \"dbg\", \"dbug\"],\n        [\"i\", \"in\", \"inf\", \"info\"],\n        [\"w\", \"wn\", \"wrn\", \"warn\"],\n        [\"e\", \"er\", \"err\", \"eror\"],\n        [\"f\", \"fa\", \"ftl\", \"fatl\"]\n    ];\n\n    static readonly string[][] UppercaseLevelMap =\n    [\n        [\"V\", \"VB\", \"VRB\", \"VERB\"],\n        [\"D\", \"DE\", \"DBG\", \"DBUG\"],\n        [\"I\", \"IN\", \"INF\", \"INFO\"],\n        [\"W\", \"WN\", \"WRN\", \"WARN\"],\n        [\"E\", \"ER\", \"ERR\", \"EROR\"],\n        [\"F\", \"FA\", \"FTL\", \"FATL\"]\n    ];\n\n    public static string GetLevelMoniker(LogEventLevel value, string? format)\n    {\n        if (format == null)\n            return value.ToString();\n\n        if (format.Length != 2 && format.Length != 3)\n            return Casing.Format(value.ToString(), format);\n\n        // Using int.Parse() here requires allocating a string to exclude the first character prefix.\n        // Junk like \"wxy\" will be accepted but produce benign results.\n        var width = format[1] - '0';\n        if (format.Length == 3)\n        {\n            width *= 10;\n            width += format[2] - '0';\n        }\n\n        if (width < 1)\n            return string.Empty;\n\n        if (width > 4)\n        {\n            var stringValue = value.ToString();\n            if (stringValue.Length > width)\n                stringValue = stringValue.Substring(0, width);\n            return Casing.Format(stringValue);\n        }\n\n        var index = (int)value;\n        if (index >= 0 && index <= (int)LogEventLevel.Fatal)\n        {\n            switch (format[0])\n            {\n                case 'w':\n                    return LowercaseLevelMap[index][width - 1];\n                case 'u':\n                    return UppercaseLevelMap[index][width - 1];\n                case 't':\n                    return TitleCaseLevelMap[index][width - 1];\n            }\n        }\n\n        return Casing.Format(value.ToString(), format);\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Templates/Rendering/Padding.cs",
    "content": "﻿// Copyright 2013-2020 Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing Serilog.Parsing;\n\nnamespace Serilog.Templates.Rendering;\n\nstatic class Padding\n{\n    static readonly char[] PaddingChars = Enumerable.Repeat(' ', 80).ToArray();\n\n    /// <summary>\n    /// Writes the provided value to the output, applying direction-based padding when <paramref name=\"alignment\"/> is provided.\n    /// </summary>\n    public static void Apply(TextWriter output, string value, Alignment alignment)\n    {\n        if (value.Length >= alignment.Width)\n        {\n            output.Write(value);\n            return;\n        }\n\n        var pad = alignment.Width - value.Length;\n\n        if (alignment.Direction == AlignmentDirection.Left)\n            output.Write(value);\n\n        if (pad <= PaddingChars.Length)\n        {\n            output.Write(PaddingChars, 0, pad);\n        }\n        else\n        {\n            output.Write(new string(' ', pad));\n        }\n\n        if (alignment.Direction == AlignmentDirection.Right)\n            output.Write(value);\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Templates/Themes/Style.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nnamespace Serilog.Templates.Themes;\n\nreadonly struct Style\n{\n    readonly string? _ansiStyle;\n\n    public Style(string ansiStyle)\n    {\n        _ansiStyle = ansiStyle;\n    }\n\n    internal StyleReset Set(TextWriter output, ref int invisibleCharacterCount)\n    {\n        if (_ansiStyle != null)\n        {\n            output.Write(_ansiStyle);\n            invisibleCharacterCount += _ansiStyle.Length;\n            invisibleCharacterCount += StyleReset.ResetCharCount;\n\n            return new(output);\n        }\n\n        return default;\n    }\n\n    public string? GetAnsiStyle()\n    {\n        return _ansiStyle;\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Templates/Themes/StyleReset.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nnamespace Serilog.Templates.Themes;\n\nreadonly struct StyleReset : IDisposable\n{\n    const string AnsiStyleResetSequence = \"\\x1b[0m\";\n    public const int ResetCharCount = 4;\n\n    readonly TextWriter? _output;\n\n    public StyleReset(TextWriter output)\n    {\n        _output = output;\n    }\n\n    public void Dispose()\n    {\n        _output?.Write(AnsiStyleResetSequence);\n    }\n}"
  },
  {
    "path": "src/Serilog.Expressions/Templates/Themes/TemplateTheme.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nnamespace Serilog.Templates.Themes;\n\n/// <summary>\n/// A template theme using the ANSI terminal escape sequences.\n/// </summary>\npublic class TemplateTheme\n{\n    /// <summary>\n    /// A 256-color theme along the lines of Visual Studio Code.\n    /// </summary>\n    public static TemplateTheme Code { get; } = TemplateThemes.Code;\n\n    /// <summary>\n    /// A theme using only gray, black and white.\n    /// </summary>\n    public static TemplateTheme Grayscale { get; } = TemplateThemes.Grayscale;\n\n    /// <summary>\n    /// A theme in the style of the original <i>Serilog.Sinks.Literate</i>.\n    /// </summary>\n    public static TemplateTheme Literate { get; } = TemplateThemes.Literate;\n\n    /// <summary>\n    /// A theme in the style of the original <i>Serilog.Sinks.Literate</i> using only standard 16 terminal colors that will work on light backgrounds.\n    /// </summary>\n    public static TemplateTheme Sixteen { get; } = TemplateThemes.Sixteen;\n\n    internal static TemplateTheme None { get; } = new(new Dictionary<TemplateThemeStyle, string>());\n\n    readonly Dictionary<TemplateThemeStyle, Style> _styles;\n\n    /// <summary>\n    /// Construct a theme given a set of styles.\n    /// </summary>\n    /// <param name=\"ansiStyles\">Styles to apply within the theme. The dictionary maps style names to ANSI\n    /// sequences implementing the styles.</param>\n    /// <exception cref=\"ArgumentNullException\">When <paramref name=\"ansiStyles\"/> is <code>null</code></exception>\n    public TemplateTheme(IReadOnlyDictionary<TemplateThemeStyle, string> ansiStyles)\n    {\n        if (ansiStyles is null) throw new ArgumentNullException(nameof(ansiStyles));\n        _styles = ansiStyles.ToDictionary(kv => kv.Key, kv => new Style(kv.Value));\n    }\n\n    /// <summary>\n    /// Construct a theme given a set of styles.\n    /// </summary>\n    /// <param name=\"baseTheme\">A base template theme, which will supply styles not overridden in <paramref name=\"ansiStyles\"/>.</param>\n    /// <param name=\"ansiStyles\">Styles to apply within the theme. The dictionary maps style names to ANSI\n    /// sequences implementing the styles.</param>\n    /// <exception cref=\"ArgumentNullException\">When <paramref name=\"ansiStyles\"/> is <code>null</code></exception>\n    public TemplateTheme(TemplateTheme baseTheme, IReadOnlyDictionary<TemplateThemeStyle, string> ansiStyles)\n    {\n        if (baseTheme == null) throw new ArgumentNullException(nameof(baseTheme));\n        if (ansiStyles is null) throw new ArgumentNullException(nameof(ansiStyles));\n        _styles = new(baseTheme._styles);\n        foreach (var kv in ansiStyles)\n            _styles[kv.Key] = new(kv.Value);\n    }\n\n    internal Style GetStyle(TemplateThemeStyle templateThemeStyle)\n    {\n        _styles.TryGetValue(templateThemeStyle, out var style);\n        return style;\n    }\n}\n"
  },
  {
    "path": "src/Serilog.Expressions/Templates/Themes/TemplateThemeStyle.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nnamespace Serilog.Templates.Themes;\n\n/// <summary>\n/// Elements styled by a template theme.\n/// </summary>\npublic enum TemplateThemeStyle\n{\n    /// <summary>\n    /// Prominent text, generally content within an event's message.\n    /// </summary>\n    Text,\n\n    /// <summary>\n    /// Boilerplate text, for example items specified in an output template.\n    /// </summary>\n    SecondaryText,\n\n    /// <summary>\n    /// De-emphasized text, for example literal text in output templates and\n    /// punctuation used when writing structured data.\n    /// </summary>\n    TertiaryText,\n\n    /// <summary>\n    /// Output demonstrating some kind of configuration issue, e.g. an invalid\n    /// message template token.\n    /// </summary>\n    Invalid,\n\n    /// <summary>\n    /// The built-in <see langword=\"null\"/> value.\n    /// </summary>\n    Null,\n\n    /// <summary>\n    /// Property and type names.\n    /// </summary>\n    Name,\n\n    /// <summary>\n    /// Strings.\n    /// </summary>\n    String,\n\n    /// <summary>\n    /// Numbers.\n    /// </summary>\n    Number,\n\n    /// <summary>\n    /// <see cref=\"bool\"/> values.\n    /// </summary>\n    Boolean,\n\n    /// <summary>\n    /// All other scalar values, e.g. <see cref=\"System.Guid\"/> instances.\n    /// </summary>\n    Scalar,\n\n    /// <summary>\n    /// Level indicator.\n    /// </summary>\n    LevelVerbose,\n\n    /// <summary>\n    /// Level indicator.\n    /// </summary>\n    LevelDebug,\n\n    /// <summary>\n    /// Level indicator.\n    /// </summary>\n    LevelInformation,\n\n    /// <summary>\n    /// Level indicator.\n    /// </summary>\n    LevelWarning,\n\n    /// <summary>\n    /// Level indicator.\n    /// </summary>\n    LevelError,\n\n    /// <summary>\n    /// Level indicator.\n    /// </summary>\n    LevelFatal,\n}"
  },
  {
    "path": "src/Serilog.Expressions/Templates/Themes/TemplateThemes.cs",
    "content": "// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nnamespace Serilog.Templates.Themes;\n\nstatic class TemplateThemes\n{\n    public static TemplateTheme Literate { get; } = new(\n        new Dictionary<TemplateThemeStyle, string>\n        {\n            [TemplateThemeStyle.Text] = \"\\x1b[38;5;0015m\",\n            [TemplateThemeStyle.SecondaryText] = \"\\x1b[38;5;0007m\",\n            [TemplateThemeStyle.TertiaryText] = \"\\x1b[38;5;0008m\",\n            [TemplateThemeStyle.Invalid] = \"\\x1b[38;5;0011m\",\n            [TemplateThemeStyle.Null] = \"\\x1b[38;5;0027m\",\n            [TemplateThemeStyle.Name] = \"\\x1b[38;5;0007m\",\n            [TemplateThemeStyle.String] = \"\\x1b[38;5;0045m\",\n            [TemplateThemeStyle.Number] = \"\\x1b[38;5;0200m\",\n            [TemplateThemeStyle.Boolean] = \"\\x1b[38;5;0027m\",\n            [TemplateThemeStyle.Scalar] = \"\\x1b[38;5;0085m\",\n            [TemplateThemeStyle.LevelVerbose] = \"\\x1b[38;5;0007m\",\n            [TemplateThemeStyle.LevelDebug] = \"\\x1b[38;5;0007m\",\n            [TemplateThemeStyle.LevelInformation] = \"\\x1b[38;5;0015m\",\n            [TemplateThemeStyle.LevelWarning] = \"\\x1b[38;5;0011m\",\n            [TemplateThemeStyle.LevelError] = \"\\x1b[38;5;0015m\\x1b[48;5;0196m\",\n            [TemplateThemeStyle.LevelFatal] = \"\\x1b[38;5;0015m\\x1b[48;5;0196m\",\n        });\n\n    public static TemplateTheme Grayscale { get; } = new(\n        new Dictionary<TemplateThemeStyle, string>\n        {\n            [TemplateThemeStyle.Text] = \"\\x1b[37;1m\",\n            [TemplateThemeStyle.SecondaryText] = \"\\x1b[37m\",\n            [TemplateThemeStyle.TertiaryText] = \"\\x1b[30;1m\",\n            [TemplateThemeStyle.Invalid] = \"\\x1b[37;1m\\x1b[47m\",\n            [TemplateThemeStyle.Null] = \"\\x1b[1m\\x1b[37;1m\",\n            [TemplateThemeStyle.Name] = \"\\x1b[37m\",\n            [TemplateThemeStyle.String] = \"\\x1b[1m\\x1b[37;1m\",\n            [TemplateThemeStyle.Number] = \"\\x1b[1m\\x1b[37;1m\",\n            [TemplateThemeStyle.Boolean] = \"\\x1b[1m\\x1b[37;1m\",\n            [TemplateThemeStyle.Scalar] = \"\\x1b[1m\\x1b[37;1m\",\n            [TemplateThemeStyle.LevelVerbose] = \"\\x1b[30;1m\",\n            [TemplateThemeStyle.LevelDebug] = \"\\x1b[30;1m\",\n            [TemplateThemeStyle.LevelInformation] = \"\\x1b[37;1m\",\n            [TemplateThemeStyle.LevelWarning] = \"\\x1b[37;1m\\x1b[47m\",\n            [TemplateThemeStyle.LevelError] = \"\\x1b[30m\\x1b[47m\",\n            [TemplateThemeStyle.LevelFatal] = \"\\x1b[30m\\x1b[47m\",\n        });\n\n    public static TemplateTheme Code { get; } = new(\n        new Dictionary<TemplateThemeStyle, string>\n        {\n            [TemplateThemeStyle.Text] = \"\\x1b[38;5;0253m\",\n            [TemplateThemeStyle.SecondaryText] = \"\\x1b[38;5;0246m\",\n            [TemplateThemeStyle.TertiaryText] = \"\\x1b[38;5;0242m\",\n            [TemplateThemeStyle.Invalid] = \"\\x1b[33;1m\",\n            [TemplateThemeStyle.Null] = \"\\x1b[38;5;0038m\",\n            [TemplateThemeStyle.Name] = \"\\x1b[38;5;0081m\",\n            [TemplateThemeStyle.String] = \"\\x1b[38;5;0216m\",\n            [TemplateThemeStyle.Number] = \"\\x1b[38;5;151m\",\n            [TemplateThemeStyle.Boolean] = \"\\x1b[38;5;0038m\",\n            [TemplateThemeStyle.Scalar] = \"\\x1b[38;5;0079m\",\n            [TemplateThemeStyle.LevelVerbose] = \"\\x1b[37m\",\n            [TemplateThemeStyle.LevelDebug] = \"\\x1b[37m\",\n            [TemplateThemeStyle.LevelInformation] = \"\\x1b[37;1m\",\n            [TemplateThemeStyle.LevelWarning] = \"\\x1b[38;5;0229m\",\n            [TemplateThemeStyle.LevelError] = \"\\x1b[38;5;0197m\\x1b[48;5;0238m\",\n            [TemplateThemeStyle.LevelFatal] = \"\\x1b[38;5;0197m\\x1b[48;5;0238m\",\n        });\n\n    public static TemplateTheme Sixteen { get; } = new(\n        new Dictionary<TemplateThemeStyle, string>\n        {\n            [TemplateThemeStyle.Text] = string.Empty,\n            [TemplateThemeStyle.SecondaryText] = string.Empty,\n            [TemplateThemeStyle.TertiaryText] = string.Empty,\n            [TemplateThemeStyle.Invalid] = \"\\x1b[33m\",\n            [TemplateThemeStyle.Null] = \"\\x1b[34m\",\n            [TemplateThemeStyle.Name] = string.Empty,\n            [TemplateThemeStyle.String] = \"\\x1b[36m\",\n            [TemplateThemeStyle.Number] = \"\\x1b[35m\",\n            [TemplateThemeStyle.Boolean] = \"\\x1b[34m\",\n            [TemplateThemeStyle.Scalar] = \"\\x1b[32m\",\n            [TemplateThemeStyle.LevelVerbose] = \"\\x1b[30;1m\",\n            [TemplateThemeStyle.LevelDebug] = \"\\x1b[1m\",\n            [TemplateThemeStyle.LevelInformation] = \"\\x1b[36;1m\",\n            [TemplateThemeStyle.LevelWarning] = \"\\x1b[33;1m\",\n            [TemplateThemeStyle.LevelError] = \"\\x1b[31;1m\",\n            [TemplateThemeStyle.LevelFatal] = \"\\x1b[31;1m\",\n        });\n}\n"
  },
  {
    "path": "src/Serilog.Expressions/Templates/Themes/ThemedJsonValueFormatter.cs",
    "content": "﻿// Copyright © Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing System.Globalization;\nusing Serilog.Data;\nusing Serilog.Events;\nusing Serilog.Formatting.Json;\n\n// ReSharper disable ForCanBeConvertedToForeach\n\nnamespace Serilog.Templates.Themes;\n\nclass ThemedJsonValueFormatter : LogEventPropertyValueVisitor<TextWriter, int>\n{\n    const string TypeTagPropertyName = \"$type\";\n\n    readonly Style _null, _bool, _num, _string, _scalar, _tertiary, _name;\n\n    public ThemedJsonValueFormatter(TemplateTheme theme)\n    {\n        _null = theme.GetStyle(TemplateThemeStyle.Null);\n        _bool = theme.GetStyle(TemplateThemeStyle.Boolean);\n        _num = theme.GetStyle(TemplateThemeStyle.Number);\n        _string = theme.GetStyle(TemplateThemeStyle.String);\n        _scalar = theme.GetStyle(TemplateThemeStyle.Scalar);\n        _tertiary = theme.GetStyle(TemplateThemeStyle.TertiaryText);\n        _name = theme.GetStyle(TemplateThemeStyle.Name);\n    }\n\n    public int Format(LogEventPropertyValue value, TextWriter output)\n    {\n        return Visit(output, value);\n    }\n\n    protected override int VisitScalarValue(TextWriter state, ScalarValue scalar)\n    {\n        return FormatLiteralValue(scalar, state);\n    }\n\n    protected override int VisitSequenceValue(TextWriter state, SequenceValue sequence)\n    {\n        var count = 0;\n\n        using (_tertiary.Set(state, ref count))\n            state.Write('[');\n\n        var delim = string.Empty;\n        for (var index = 0; index < sequence.Elements.Count; ++index)\n        {\n            if (delim.Length != 0)\n            {\n                using (_tertiary.Set(state, ref count))\n                    state.Write(delim);\n            }\n\n            delim = \",\";\n            count += Visit(state, sequence.Elements[index]);\n        }\n\n        using (_tertiary.Set(state, ref count))\n            state.Write(']');\n\n        return count;\n\n    }\n\n    protected override int VisitStructureValue(TextWriter state, StructureValue structure)\n    {\n        var count = 0;\n\n        using (_tertiary.Set(state, ref count))\n            state.Write('{');\n\n        var delim = string.Empty;\n        for (var index = 0; index < structure.Properties.Count; ++index)\n        {\n            if (delim.Length != 0)\n            {\n                using (_tertiary.Set(state, ref count))\n                    state.Write(delim);\n            }\n\n            delim = \",\";\n\n            var property = structure.Properties[index];\n\n            using (_name.Set(state, ref count))\n                JsonValueFormatter.WriteQuotedJsonString(property.Name, state);\n\n            using (_tertiary.Set(state, ref count))\n                state.Write(\":\");\n\n            count += Visit(state, property.Value);\n        }\n\n        if (structure.TypeTag != null)\n        {\n            using (_tertiary.Set(state, ref count))\n                state.Write(delim);\n\n            using (_name.Set(state, ref count))\n                JsonValueFormatter.WriteQuotedJsonString(TypeTagPropertyName, state);\n\n            using (_tertiary.Set(state, ref count))\n                state.Write(\":\");\n\n            using (_string.Set(state, ref count))\n                JsonValueFormatter.WriteQuotedJsonString(structure.TypeTag, state);\n        }\n\n        using (_tertiary.Set(state, ref count))\n            state.Write('}');\n\n        return count;\n    }\n\n    protected override int VisitDictionaryValue(TextWriter state, DictionaryValue dictionary)\n    {\n        var count = 0;\n\n        using (_tertiary.Set(state, ref count))\n            state.Write('{');\n\n        var delim = string.Empty;\n        foreach (var element in dictionary.Elements)\n        {\n            if (delim.Length != 0)\n            {\n                using (_tertiary.Set(state, ref count))\n                    state.Write(delim);\n            }\n\n            delim = \",\";\n\n            var style = element.Key.Value == null\n                ? _null\n                : element.Key.Value is string\n                    ? _string\n                    : _scalar;\n\n            using (style.Set(state, ref count))\n                JsonValueFormatter.WriteQuotedJsonString(element.Key.Value?.ToString() ?? \"null\", state);\n\n            using (_tertiary.Set(state, ref count))\n                state.Write(\":\");\n\n            count += Visit(state, element.Value);\n        }\n\n        using (_tertiary.Set(state, ref count))\n            state.Write('}');\n\n        return count;\n    }\n\n    int FormatLiteralValue(ScalarValue scalar, TextWriter output)\n    {\n        var value = scalar.Value;\n        var count = 0;\n\n        if (value == null)\n        {\n            using (_null.Set(output, ref count))\n                output.Write(\"null\");\n            return count;\n        }\n\n        if (value is string str)\n        {\n            using (_string.Set(output, ref count))\n                JsonValueFormatter.WriteQuotedJsonString(str, output);\n            return count;\n        }\n\n        if (value is ValueType)\n        {\n            if (value is int or uint or long or ulong or decimal or byte or sbyte or short or ushort)\n            {\n                using (_num.Set(output, ref count))\n                    output.Write(((IFormattable)value).ToString(null, CultureInfo.InvariantCulture));\n                return count;\n            }\n\n            if (value is double d)\n            {\n                using (_num.Set(output, ref count))\n                {\n                    if (double.IsNaN(d) || double.IsInfinity(d))\n                        JsonValueFormatter.WriteQuotedJsonString(d.ToString(CultureInfo.InvariantCulture), output);\n                    else\n                        output.Write(d.ToString(\"R\", CultureInfo.InvariantCulture));\n                }\n                return count;\n            }\n\n            if (value is float f)\n            {\n                using (_num.Set(output, ref count))\n                {\n                    if (double.IsNaN(f) || double.IsInfinity(f))\n                        JsonValueFormatter.WriteQuotedJsonString(f.ToString(CultureInfo.InvariantCulture), output);\n                    else\n                        output.Write(f.ToString(\"R\", CultureInfo.InvariantCulture));\n                }\n                return count;\n            }\n\n            if (value is bool b)\n            {\n                using (_bool.Set(output, ref count))\n                    output.Write(b ? \"true\" : \"false\");\n\n                return count;\n            }\n\n            if (value is char ch)\n            {\n                using (_scalar.Set(output, ref count))\n                    JsonValueFormatter.WriteQuotedJsonString(ch.ToString(), output);\n                return count;\n            }\n\n            if (value is DateTime or DateTimeOffset)\n            {\n                using (_scalar.Set(output, ref count))\n                {\n                    output.Write('\"');\n                    output.Write(((IFormattable)value).ToString(\"O\", CultureInfo.InvariantCulture));\n                    output.Write('\"');\n                }\n                return count;\n            }\n        }\n\n        using (_scalar.Set(output, ref count))\n            JsonValueFormatter.WriteQuotedJsonString(value.ToString() ?? \"\", output);\n\n        return count;\n    }\n}"
  },
  {
    "path": "test/Serilog.Expressions.PerformanceTests/ComparisonBenchmark.cs",
    "content": "using BenchmarkDotNet.Attributes;\nusing Serilog.Events;\nusing Serilog.Expressions.PerformanceTests.Support;\nusing Xunit;\n\nnamespace Serilog.Expressions.PerformanceTests;\n\n/// <summary>\n/// Tests the performance of various filtering mechanisms.\n/// </summary>\npublic class ComparisonBenchmark\n{\n    readonly Func<LogEvent, bool> _trivialFilter, _handwrittenFilter, _expressionFilter;\n    readonly LogEvent _event = Some.InformationEvent(\"{A}\", 3);\n\n    public ComparisonBenchmark()\n    {\n        // Just the delegate invocation overhead\n        _trivialFilter = _ => true;\n\n        // `A == 3`, the old way\n        _handwrittenFilter = evt =>\n        {\n            if (evt.Properties.TryGetValue(\"A\", out var a) && (a as ScalarValue)?.Value is int)\n            {\n                return (int)((ScalarValue)a).Value! == 3;\n            }\n\n            return false;\n        };\n\n        // The code we're interested in; the `true.Equals()` overhead is normally added when\n        // this is used with Serilog\n        var compiled = SerilogExpression.Compile(\"A = 3\");\n        _expressionFilter = evt => ExpressionResult.IsTrue(compiled(evt));\n\n        Assert.True(_trivialFilter(_event) && _handwrittenFilter(_event) && _expressionFilter(_event));\n    }\n\n    [Benchmark]\n    public void TrivialFilter()\n    {\n        _trivialFilter(_event);\n    }\n\n    [Benchmark(Baseline = true)]\n    public void HandwrittenFilter()\n    {\n        _handwrittenFilter(_event);\n    }\n\n    [Benchmark]\n    public void ExpressionFilter()\n    {\n        _expressionFilter(_event);\n    }\n}"
  },
  {
    "path": "test/Serilog.Expressions.PerformanceTests/Harness.cs",
    "content": "\n// Copyright 2013-2016 Serilog Contributors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nusing BenchmarkDotNet.Running;\nusing Serilog.Expressions.PerformanceTests;\nusing Xunit;\n\nnamespace Serilog.PerformanceTests;\n\npublic class Harness\n{\n    [Fact]\n    public void ComparisonBenchmark()\n    {\n        BenchmarkRunner.Run<ComparisonBenchmark>();\n    }\n}"
  },
  {
    "path": "test/Serilog.Expressions.PerformanceTests/Serilog.Expressions.PerformanceTests.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>net9.0</TargetFramework>\n    <GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>\n    <GenerateDocumentationFile>false</GenerateDocumentationFile>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Microsoft.NET.Test.Sdk\" Version=\"17.13.0\" />\n    <PackageReference Include=\"xunit.runner.visualstudio\" Version=\"3.0.2\">\n      <PrivateAssets>all</PrivateAssets>\n      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>\n    </PackageReference>\n    <PackageReference Include=\"xunit\" Version=\"2.9.3\" />\n    <PackageReference Include=\"BenchmarkDotNet\" Version=\"0.14.0\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"../../src/Serilog.Expressions/Serilog.Expressions.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "test/Serilog.Expressions.PerformanceTests/Support/Some.cs",
    "content": "﻿using Serilog.Events;\nusing Xunit.Sdk;\n\nnamespace Serilog.Expressions.PerformanceTests.Support;\n\nstatic class Some\n{\n    public static LogEvent InformationEvent(string messageTemplate = \"Hello, world!\", params object[] propertyValues)\n    {\n        return LogEvent(LogEventLevel.Information, messageTemplate, propertyValues);\n    }\n\n    public static LogEvent WarningEvent(string messageTemplate = \"Hello, world!\", params object[] propertyValues)\n    {\n        return LogEvent(LogEventLevel.Warning, messageTemplate, propertyValues);\n    }\n\n    public static LogEvent LogEvent(LogEventLevel level, string messageTemplate = \"Hello, world!\", params object[] propertyValues)\n    {\n        var log = new LoggerConfiguration().CreateLogger();\n#pragma warning disable Serilog004 // Constant MessageTemplate verifier\n        if (!log.BindMessageTemplate(messageTemplate, propertyValues, out var template, out var properties))\n#pragma warning restore Serilog004 // Constant MessageTemplate verifier\n        {\n            throw new XunitException(\"Template could not be bound.\");\n        }\n\n        return new(DateTimeOffset.Now, level, null, template, properties);\n    }\n}"
  },
  {
    "path": "test/Serilog.Expressions.Tests/Cases/expression-evaluation-cases.asv",
    "content": "// Primitives\nnull                                 ⇶ null\ntrue                                 ⇶ true\nfalse                                ⇶ false\n\n// Arrays\n[]                                   ⇶ []\n[5, 6]                               ⇶ [5, 6]\n[5, undefined()]                     ⇶ [5]\n[1]                                  ⇶ [1]\n[[1]]                                ⇶ [[1]]\n[1, ..[2, 3], 4, ..[undefined(), 5]] ⇶ [1, 2, 3, 4, 5]\n\n// Objects\n{}                                   ⇶ {}\n{a: 1, 'b c': 2}                     ⇶ {a: 1, 'b c': 2}\n{User}                               ⇶ {User: {Id: 42, Name: 'nblumhardt'}}\n{@l}                                 ⇶ {'@l': @l}\n{a: 1, a: 2}                         ⇶ {a: 2}\n{a: 1, b: undefined()}               ⇶ {a: 1}\n{a: 1, a: undefined()}               ⇶ {}\n{a: 1, ..{b: 2, c: 3}}               ⇶ {a: 1, b: 2, c: 3}\n{a: 1, ..{b: 2}, a: undefined()}     ⇶ {b: 2}\n{a: 1, ..{b: 2}, b: undefined()}     ⇶ {a: 1}\n{a: 1, ..{a: undefined()}}           ⇶ {a: 1}\n{..{a: 1}, ..{b: 2, c: 3}}           ⇶ {a: 1, b: 2, c: 3}\n{a: 1}['a']                          ⇶ 1\n{a: 1}['A']                          ⇶ undefined()\n{a: 1}.a                             ⇶ 1\n{a: 1}.A                             ⇶ undefined()\nElementAt({a: 1}, 'A')               ⇶ undefined()\nElementAt({a: 1}, 'A') ci            ⇶ 1\n\n// Strings\n''                                   ⇶ ''\n'foo'                                ⇶ 'foo'\n''''                                 ⇶ ''''\n'a'                                  ⇶ 'a'\n'''b'                                ⇶ '''b'\n'b'''                                ⇶ 'b'''\n'a''b'                               ⇶ 'a''b'\n'😂'                                 ⇶ '😂'\n\n// Numbers\n0                                    ⇶ 0\n0.0                                  ⇶ 0\n1                                    ⇶ 1\n1.0                                  ⇶ 1\n123                                  ⇶ 123\n1.23                                 ⇶ 1.23\n-1                                   ⇶ -1\n-1.23                                ⇶ -1.23\n\n// Math\n1 + 1                                ⇶ 2\n1 + 2 + 3                            ⇶ 6\n'foo' + 1                            ⇶ undefined()\n1 - 2                                ⇶ -1\n-1 - -1                              ⇶ 0\n3 - 2                                ⇶ 1\n'foo' - - 1                          ⇶ undefined()\n1.0 + 2.0                            ⇶ 3\n1 * 0                                ⇶ 0\n12 * 12                              ⇶ 144\n3 / 1                                ⇶ 3\n3 / 0                                ⇶ undefined()\n3 ^ 2                                ⇶ 9\n0 ^ 1                                ⇶ 0\n0 ^ 0                                ⇶ 1\n0 % 0                                ⇶ undefined()\n0 % 1                                ⇶ 0\n1 % 0                                ⇶ undefined()\n1 % 1                                ⇶ 0\n5 % 2                                ⇶ 1\n79228162514264337593543950334 + 1    ⇶ 79228162514264337593543950335\nround(1.4343434343, 2)               ⇶ 1.43\nround(1.435, 2)                      ⇶ 1.44\nround(0.00000000, 5)                 ⇶ 0\nround(0.99999999, 5)                 ⇶ 1\n52 ^ -3                              ⇶ 0.00000711197086936732\n2 ^ 3.14                             ⇶ 8.81524092701289\n\n// Boolean logic\ntrue or false and false              ⇶ true\n(true or false) and false            ⇶ false\nnot false and false                  ⇶ false\nnot (false and false)                ⇶ true\nfalse and true                       ⇶ false\nfalse and false                      ⇶ false\ntrue and true                        ⇶ true\ntrue and null                        ⇶ false\nfalse and null                       ⇶ false\nnull and null                        ⇶ false\nundefined() and undefined()          ⇶ false\nundefined() and true                 ⇶ false\ntrue and undefined()                 ⇶ false\nfalse or false                       ⇶ false\ntrue or false                        ⇶ true\ntrue or true                         ⇶ true\nfalse or true                        ⇶ true\ntrue or null                         ⇶ true\nnull or true                         ⇶ true\ntrue or undefined()                  ⇶ true\nundefined() or true                  ⇶ true\nundefined() or undefined()           ⇶ false\nnot true                             ⇶ false\nnot false                            ⇶ true\nnot undefined()                      ⇶ true\nnot(true)                            ⇶ false\nnot(false and false)                 ⇶ true\nnot(undefined())                     ⇶ true\n'not a bool' or true                 ⇶ true\n\n// Coalesce\ncoalesce(null, 1)                    ⇶ 1\ncoalesce(undefined(), 1)             ⇶ 1\ncoalesce(1, null)                    ⇶ 1\ncoalesce(undefined(), undefined())   ⇶ undefined()\ncoalesce(null, null)                 ⇶ null\ncoalesce(null, null, 3)              ⇶ 3\ncoalesce()                           ⇶ undefined()\ncoalesce(1)                          ⇶ 1\n\n// Identifiers\nA                                    ⇶ undefined()\n_a                                   ⇶ undefined()\na1                                   ⇶ undefined()\n\n// EQ\n1 = 1                                ⇶ true\n1 = 0                                ⇶ false\nnull = 0                             ⇶ false\nnull = null                          ⇶ true\nnull = undefined()                   ⇶ undefined()\nundefined() = undefined()            ⇶ undefined()\n\n// NEQ\n1 <> 1                               ⇶ false\n1 <> 0                               ⇶ true\nnull <> 0                            ⇶ true\nnull <> null                         ⇶ false\nundefined() <> null                  ⇶ undefined()\nundefined() <> undefined()           ⇶ undefined()\n\n// LT\n1 < 2                                ⇶ true\n1 < 1                                ⇶ false\n1 < 0                                ⇶ false\nnull < 0                             ⇶ undefined()\nnull < null                          ⇶ undefined()\nundefined() < null                   ⇶ undefined()\nundefined() < undefined()            ⇶ undefined()\n\n// LTE\n1 <= 2                               ⇶ true\n1 <= 1                               ⇶ true\n1 <= 0                               ⇶ false\nnull <= 0                            ⇶ undefined()\nnull <= null                         ⇶ undefined()\nundefined() <= null                  ⇶ undefined()\nundefined() <= undefined()           ⇶ undefined()\n\n// GT\n3 > 1                                ⇶ true\n1 > 1                                ⇶ false\n1 > 0                                ⇶ true\nnull > 0                             ⇶ undefined()\nnull > null                          ⇶ undefined()\nundefined() > null                   ⇶ undefined()\nundefined() > undefined()            ⇶ undefined()\n\n// GTE\n2 >= 1                               ⇶ true\n1 >= 1                               ⇶ true\n1 >= 0                               ⇶ true\n-1 >= 0                              ⇶ false\nnull >= 0                            ⇶ undefined()\nnull >= null                         ⇶ undefined()\nundefined() >= null                  ⇶ undefined()\nundefined() >= undefined()           ⇶ undefined()\n\n// in/not in\n1 in [1, 2, 3]                       ⇶ true\n5 in [1, 2, 3]                       ⇶ false\n1 not in [1, 2, 3]                   ⇶ false\n5 not in [1, 2, 3]                   ⇶ true\nundefined() in [1, 2, 3]             ⇶ undefined()\nundefined() not in [1, 2, 3]         ⇶ undefined()\n1 in undefined()                     ⇶ undefined()\nnull in [1, null, 3]                 ⇶ true\n\n// is null/is not null\nnull is null                         ⇶ true\nnull is not null                     ⇶ false\nundefined() is null                  ⇶ true\n10 is null                           ⇶ false\n\n// Property names and accessors\n[5, 6, 7][1]                         ⇶ 6\nUser.Name                            ⇶ 'nblumhardt'\n{Name: 'nblumhardt'}.Name            ⇶ 'nblumhardt'\n\n// Wildcards\n[1,2,3][?] > 2                       ⇶ true\n[1,2,3][*] > 2                       ⇶ false\n{k:'test'}[?] = 'test'               ⇶ true\n{k:'test'}[?] like 'test'            ⇶ true\n{k:'test'}[?] like 'TEST'            ⇶ false\n{k:'test'}[?] like 'TEST' ci         ⇶ true\n{k:'test'}[?] like '%TES%' ci        ⇶ true\n{k:'test'}[?] = 'none'               ⇶ false\ntest_dict({k:'test'})[?] = 'test'               ⇶ true\ntest_dict({k:'test'})[?] like 'test'            ⇶ true\ntest_dict({k:'test'})[?] like 'TEST'            ⇶ false\ntest_dict({k:'test'})[?] like 'TEST' ci         ⇶ true\ntest_dict({k:'test'})[?] like '%TES%' ci        ⇶ true\ntest_dict({k:'test'})[?] = 'none'               ⇶ false\n\n// Text and regex\nismatch('foo', 'f')                  ⇶ true\nindexofmatch('foo', 'o')             ⇶ 1\nismatch('foo', '^f')                 ⇶ true\nismatch('foo', 'F')                  ⇶ false\nismatch('foo', 'F') ci               ⇶ true\nismatch('foo', '^o')                 ⇶ false\nindexofmatch('foo', 'x')             ⇶ -1\nsubstring('abcd', 1, 2)              ⇶ 'bc'\nsubstring('abcd', 1)                 ⇶ 'bcd'\nconcat('a', 'b', 'c')                ⇶ 'abc'\nconcat('a', 42, 'c')                 ⇶ undefined()\nconcat('a', undefined())             ⇶ undefined()\nconcat(undefined(), 'b')             ⇶ undefined()\n\n// Conditional\nif true then 1 else 2                ⇶ 1\nif 1 + 2 = 3 then 1 else 2           ⇶ 1\nif false then 1 else 2               ⇶ 2\nif undefined() then 1 else 2         ⇶ 2\nif 'string' then 1 else 2            ⇶ 2\nif true then if false then 1 else 2 else 3 ⇶ 2\n\n// ToString\ntostring(16)                         ⇶ '16'\ntostring('test')                     ⇶ 'test'\ntostring({})                         ⇶ undefined()\ntostring([])                         ⇶ undefined()\ntostring(16, '000')                  ⇶ '016'\ntostring(null)                       ⇶ undefined()\ntostring(undefined())                ⇶ undefined()\ntostring('test', '000')              ⇶ 'test'\ntostring('test', [])                 ⇶ undefined()\ntostring('test', 42)                 ⇶ undefined()\ntostring(16, undefined())            ⇶ '16'\ntostring(16, null)                   ⇶ '16'\n\n// Tests are in fr-FR\ntostring(16.3)                       ⇶ '16,3'\n\n// TypeOf\ntypeof(undefined())                  ⇶ 'undefined'\ntypeof('test')                       ⇶ 'System.String'\ntypeof(10)                           ⇶ 'System.Decimal'\ntypeof(true)                         ⇶ 'System.Boolean'\ntypeof(null)                         ⇶ 'null'\ntypeof([])                           ⇶ 'array'\ntypeof({})                           ⇶ 'object'\ntypeof(@x)                           ⇶ 'System.DivideByZeroException'\n\n// UtcDateTime\ntostring(utcdatetime(now()), 'o') like '20%' ⇶ true\n\n// Case comparison\n'test' = 'TEST'                      ⇶ false\n'tschüß' = 'TSCHÜSS'                 ⇶ false\n'ὈΔΥΣΣΕΎΣ!' = 'ὀδυσσεύς!'            ⇶ false\n'ὈΔΥΣΣΕΎΣ!' = 'ὀδυσσεύσ!'            ⇶ false\n'test' = 'TEST' ci                   ⇶ true\n'tschüß' = 'TSCHÜSS' ci              ⇶ false\n'ὈΔΥΣΣΕΎΣ!' = 'ὀδυσσεύσ!' ci         ⇶ true\nnull = 0 ci                          ⇶ false\nnull = null ci                       ⇶ true\nnull = undefined() ci                ⇶ undefined()\nundefined() = undefined() ci         ⇶ undefined()\n\n// Like\n'test' like 'test'                   ⇶ true\n'test' like 'Test'                   ⇶ false\n'test' like 'Test' ci                ⇶ true\n'test' like '%st'                    ⇶ true\n'test' like '_est'                   ⇶ true\n'test' like 't%t'                    ⇶ true\n'test' like 't_st'                   ⇶ true\n'test' like 'te%'                    ⇶ true\n'test' like 'tes_'                   ⇶ true\n'test' like '%su'                    ⇶ false\n'test' like '_esu'                   ⇶ false\n'test' like 't%u'                    ⇶ false\n'test' like 't_su'                   ⇶ false\n'test' like 'ue%'                    ⇶ false\n'test' like 'ues_'                   ⇶ false\n'test' like '%s_'                    ⇶ true\n'test' like '%'                      ⇶ true\n'test' like 't%s%'                   ⇶ true\n'test' like 'es'                     ⇶ false\n'test' like ''                       ⇶ false\n'' like ''                           ⇶ true\n\n// Built-ins\n\n@m                                   ⇶ 'Hello, World!'\n@mt                                  ⇶ 'Hello, {Name}!'\ntostring(@x) like 'System.DivideByZeroException%' ⇶ true\n@l                                   ⇶ 'Warning'\n@sp                                  ⇶ 'bb1111820570b80e'\n@tr                                  ⇶ '1befc31e94b01d1a473f63a7905f6c9b'\n\n// Inspect\ninspect(@x).Message                  ⇶ 'Attempted to divide by zero.'\n\n// Replace\nreplace('xYX', 'x', 'z')           ⇶ 'zYX'\nreplace('xYX', 'x', 'z') ci        ⇶ 'zYz'\nreplace('mess', 'ss', 'an')        ⇶ 'mean'\nreplace('mess', 's', 'an')         ⇶ 'meanan'\nreplace('xyz', 'x', '$0')          ⇶ '$0yz'\nreplace('xyz', 'x', concat('$', '0'))  ⇶ '$0yz'\n\n// Nest\nnest({'a.b': 1, 'a.c': 2, 'a.c.d': 3}) ⇶ {a: {b: 1, c: 2, 'c.d': 3}}\nnest('a.b.c')                      ⇶ undefined()\n"
  },
  {
    "path": "test/Serilog.Expressions.Tests/Cases/template-encoding-cases.asv",
    "content": "{@m}                              ⇶ (Hello, nblumhardt!)\n{'world'}!                        ⇶ (world)!\n{unsafe('world')}!                ⇶ world!\n{substring('world', 0, 2)}!       ⇶ (wo)!\n|{somethingnothere}|              ⇶ |()|\n{#if true}x{'A'}x{#end}           ⇶ x(A)x\n{#each a in [1,2,3]}<{a}>{#delimit},{#end}           ⇶ <(1)>,<(2)>,<(3)>\n{ {name: 'world' } }!             ⇶ ({\"name\":\"world\"})!\n"
  },
  {
    "path": "test/Serilog.Expressions.Tests/Cases/template-evaluation-cases.asv",
    "content": "Hello, {'world'}!                        ⇶ Hello, world!\n{@l}                                     ⇶ Information\n{@l:u3}                                  ⇶ INF\n{ {level: ToString(@l, 'u3')} }          ⇶ {\"level\":\"INF\"}\nItems are {[1, 2]}                       ⇶ Items are [1,2]\nMembers are { {a: 1, 'b c': 2} }         ⇶ Members are {\"a\":1,\"b c\":2}\n{@p}                                     ⇶ {\"Name\":\"nblumhardt\"}\nHello, {'my } brackety { {}} friends'}!  ⇶ Hello, my } brackety { {}} friends!\nText only                                ⇶ Text only\n{{ Escaped {{ left {{                    ⇶ { Escaped { left {\n}} Escaped }} right }}                   ⇶ } Escaped } right }\nFormatted {42:0000}                      ⇶ Formatted 0042\nAligned {42,4}!                          ⇶ Aligned   42!\nLeft {42,-4}!                            ⇶ Left 42  !\nUnder width {42,0}!                      ⇶ Under width 42!\n{@m}                                     ⇶ Hello, nblumhardt!\nHello, {#if true}world{#end}!            ⇶ Hello, world!\nHello, {#if true}w{42}d{#end}!           ⇶ Hello, w42d!\nHello, {#if 1 = 1}world{#else}there{#end}!           ⇶ Hello, world!\nHello, {#if 1 = 2}world{#else}there{#end}!           ⇶ Hello, there!\nHello, {#if undefined()}world{#else}there{#end}!     ⇶ Hello, there!\nA{#if false}B{#else if false}C{#else if true}D{#else}E{#end} ⇶ AD\nA{#if false}B{#else if true}C{#end}                  ⇶ AC\n{#if true}A{#if false}B{#else}C{#end}D{#end}         ⇶ ACD\n{#each a in [1,2,3]}<{a}>{#delimit},{#end}           ⇶ <1>,<2>,<3>\n{#each a, i in [1,2,3]}<{a}>({i}){#delimit},{#end}   ⇶ <1>(0),<2>(1),<3>(2)\n{#each a in {x: 1, y: 2}}{a}{#end}                   ⇶ xy\n{#each a, b in {x: 1, y: 2}}{a}.{b}{#end}            ⇶ x.1y.2\n{#each a, b in {x: {y: 'z'}}}{#each c, d in b}A: {a}, C: {c}, D: {d}{#end}{#end} ⇶ A: x, C: y, D: z\n{#if true}A{#each a in [1]}B{a}{#end}C{#end}D        ⇶ AB1CD\n{#each a in []}{a}!{#else}none{#end}                 ⇶ none\nCulture-specific {42.34}                             ⇶ Culture-specific 42,34\n{rest()}                                             ⇶ {\"Name\":\"nblumhardt\"}\n{Name} {rest()}                                      ⇶ nblumhardt {}\n{rest(true)}                                         ⇶ {}\n{@t:yyyy-MM-dd HH:mm:ss.ffff zzz}                    ⇶ 2000-12-31 23:59:58.1230 +10:00\n{@t:yyyy-MM-dd HH:mm:ss.ffff zzz------------------}  ⇶ 2000-12-31 23:59:58.1230 +10:00------------------\n"
  },
  {
    "path": "test/Serilog.Expressions.Tests/Cases/translation-cases.asv",
    "content": "﻿// Like\nA like 'a'                                    ⇶ _Internal_NotEqual(_Internal_IndexOfMatch(A, '^a$'), -1)\nA like 'a%'                                   ⇶ _Internal_NotEqual(_Internal_IndexOfMatch(A, '^a'), -1)\nA like '%a%'                                  ⇶ _Internal_NotEqual(_Internal_IndexOfMatch(A, 'a'), -1)\nA like '%a'                                   ⇶ _Internal_NotEqual(_Internal_IndexOfMatch(A, 'a$'), -1)\nA like '%a_b%'                                ⇶ _Internal_NotEqual(_Internal_IndexOfMatch(A, 'a.b'), -1)\nA like 'a%b%'                                 ⇶ _Internal_NotEqual(_Internal_IndexOfMatch(A, '^a(?:.|\\r|\\n)*b'), -1)\nA like '%'                                    ⇶ _Internal_NotEqual(_Internal_IndexOfMatch(A, '.*'), -1)\n\n// Root properties\n@p['Test']                                    ⇶ Test\n@p[Test]                                      ⇶ @p[Test]\n\n// Variadics\ncoalesce(A, B, C, D)                          ⇶ coalesce(A, coalesce(B, coalesce(C, D)))\n\n// Wildcards!\nA[?]                                          ⇶ _Internal_Any(A, |$$p0| {$$p0})\nA or B[*]                                     ⇶ _Internal_Or(A, _Internal_All(B, |$$p0| {$$p0}))\nnot (A is not null) or not (A[?] = 'a')       ⇶ _Internal_Or(_Internal_Not(_Internal_IsNotNull(A)), _Internal_Not(_Internal_Any(A, |$$p0| {_Internal_Equal($$p0, 'a')})))\nA[?].B[*].C = D                               ⇶ _Internal_Any(A, |$$p1| {_Internal_All($$p1.B, |$$p0| {_Internal_Equal($$p0.C, D)})})\n"
  },
  {
    "path": "test/Serilog.Expressions.Tests/ConfigurationTests.cs",
    "content": "﻿using Serilog.Expressions.Tests.Support;\nusing Xunit;\n\nnamespace Serilog.Expressions.Tests;\n\npublic class ConfigurationTests\n{\n    [Fact]\n    public void ExpressionsControlConditionalSinks()\n    {\n        var sink = new CollectingSink();\n        var logger = new LoggerConfiguration()\n            .WriteTo.Conditional(\"A = 1 or A = 2\", wt => wt.Sink(sink))\n            .CreateLogger();\n\n        foreach (var a in Enumerable.Range(0, 5))\n            logger.Information(\"{A}\", a);\n\n        Assert.Equal(2, sink.Events.Count);\n    }\n\n    [Fact]\n    public void ExpressionsControlConditionalEnrichment()\n    {\n        var sink = new CollectingSink();\n        var logger = new LoggerConfiguration()\n            .Enrich.When(\"A = 1 or A = 2\", e => e.WithProperty(\"B\", 1))\n            .WriteTo.Sink(sink)\n            .CreateLogger();\n\n        foreach (var a in Enumerable.Range(0, 5))\n            logger.Information(\"{A}\", a);\n\n        Assert.Equal(2, sink.Events.Count(e => e.Properties.ContainsKey(\"B\")));\n    }\n}"
  },
  {
    "path": "test/Serilog.Expressions.Tests/ExpressionCompilerTests.cs",
    "content": "﻿using Serilog.Events;\nusing Serilog.Expressions.Tests.Support;\nusing Xunit;\n\n// ReSharper disable CoVariantArrayConversion\n\nnamespace Serilog.Expressions.Tests;\n\npublic class ExpressionCompilerTests\n{\n    [Fact]\n    public void ExpressionsEvaluateStringEquality()\n    {\n        AssertEvaluation(\"Fruit = 'Apple'\",\n            Some.InformationEvent(\"Snacking on {Fruit}\", \"Apple\"),\n            Some.InformationEvent(),\n            Some.InformationEvent(\"Snacking on {Fruit}\", \"Acerola\"));\n    }\n\n    [Fact]\n    public void ComparisonsAreCaseSensitive()\n    {\n        AssertEvaluation(\"Fruit = 'Apple'\",\n            Some.InformationEvent(\"Snacking on {Fruit}\", \"Apple\"),\n            Some.InformationEvent(\"Snacking on {Fruit}\", \"APPLE\"));\n    }\n\n    [Fact]\n    public void ExpressionsEvaluateStringContent()\n    {\n        AssertEvaluation(\"Fruit like '%pp%'\",\n            Some.InformationEvent(\"Snacking on {Fruit}\", \"Apple\"),\n            Some.InformationEvent(\"Snacking on {Fruit}\", \"Acerola\"));\n    }\n\n    [Fact]\n    public void ExpressionsEvaluateStringPrefix()\n    {\n        AssertEvaluation(\"Fruit like 'Ap%'\",\n            Some.InformationEvent(\"Snacking on {Fruit}\", \"Apple\"),\n            Some.InformationEvent(\"Snacking on {Fruit}\", \"Acerola\"));\n    }\n\n    [Fact]\n    public void ExpressionsEvaluateStringSuffix()\n    {\n        AssertEvaluation(\"Fruit like '%le'\",\n            Some.InformationEvent(\"Snacking on {Fruit}\", \"Apple\"),\n            Some.InformationEvent(\"Snacking on {Fruit}\", \"Acerola\"));\n    }\n\n    [Fact]\n    public void LikeIsCaseSensitive()\n    {\n        AssertEvaluation(\"Fruit like 'apple'\",\n            Some.InformationEvent(\"Snacking on {Fruit}\", \"apple\"),\n            Some.InformationEvent(\"Snacking on {Fruit}\", \"Apple\"));\n    }\n\n    [Fact]\n    public void ExpressionsEvaluateNumericComparisons()\n    {\n        AssertEvaluation(\"Volume > 11\",\n            Some.InformationEvent(\"Adding {Volume} L\", 11.5),\n            Some.InformationEvent(\"Adding {Volume} L\", 11));\n    }\n\n    [Fact]\n    public void ExpressionsEvaluateWildcardsOnCollectionItems()\n    {\n        AssertEvaluation(\"Items[?] like 'C%'\",\n            Some.InformationEvent(\"Cart contains {@Items}\", [new[] { \"Tea\", \"Coffee\" }]), // Test helper doesn't correct this case\n            Some.InformationEvent(\"Cart contains {@Items}\", [new[] { \"Apricots\" }]));\n    }\n\n    [Fact]\n    public void ExpressionsEvaluateBuiltInProperties()\n    {\n        AssertEvaluation(\"@l = 'Information'\",\n            Some.InformationEvent(),\n            Some.WarningEvent());\n    }\n\n    [Fact]\n    public void ExpressionsEvaluateExistentials()\n    {\n        AssertEvaluation(\"AppId is not null\",\n            Some.InformationEvent(\"{AppId}\", 10),\n            Some.InformationEvent(\"{AppId}\", [null]),\n            Some.InformationEvent());\n    }\n\n    [Fact]\n    public void ExpressionsLogicalOperations()\n    {\n        AssertEvaluation(\"A and B\",\n            Some.InformationEvent(\"{A} {B}\", true, true),\n            Some.InformationEvent(\"{A} {B}\", true, false),\n            Some.InformationEvent());\n    }\n\n    [Fact]\n    public void ExpressionsEvaluateSubproperties()\n    {\n        AssertEvaluation(\"Cart.Total > 10\",\n            Some.InformationEvent(\"Checking out {@Cart}\", new { Total = 20 }),\n            Some.InformationEvent(\"Checking out {@Cart}\", new { Total = 5 }));\n    }\n\n\n    [Fact]\n    public void SequenceLengthCanBeDetermined()\n    {\n        AssertEvaluation(\"length(Items) > 1\",\n            Some.InformationEvent(\"Checking out {Items}\", [new[] { \"pears\", \"apples\" }]),\n            Some.InformationEvent(\"Checking out {Items}\", [new[] { \"pears\" }]));\n    }\n\n    [Fact]\n    public void InMatchesLiterals()\n    {\n        AssertEvaluation(\"@l in ['Warning', 'Error']\",\n            Some.LogEvent(LogEventLevel.Error, \"Hello\"),\n            Some.InformationEvent(\"Hello\"));\n    }\n\n    [Fact]\n    public void InExaminesSequenceValues()\n    {\n        AssertEvaluation(\"5 not in Numbers\",\n            Some.InformationEvent(\"{Numbers}\", new []{1, 2, 3}),\n            Some.InformationEvent(\"{Numbers}\", new [] { 1, 5, 3 }),\n            Some.InformationEvent());\n    }\n\n    [Theory]\n    [InlineData(\"now(1)\", \"The function `now` accepts no arguments.\")]\n    [InlineData(\"length()\", \"The function `length` accepts one argument, `value`.\")]\n    [InlineData(\"length(1, 2)\", \"The function `length` accepts one argument, `value`.\")]\n    [InlineData(\"round()\", \"The function `round` accepts two arguments, `value` and `places`.\")]\n    [InlineData(\"substring()\", \"The function `substring` accepts arguments `value`, `startIndex`, and `length` (optional).\")]\n    public void ReportsArityMismatches(string call, string expectedError)\n    {\n        // These will eventually be reported gracefully by `TryCompile()`...\n        var ex = Assert.Throws<ArgumentException>(() => SerilogExpression.Compile(call));\n        Assert.Equal(expectedError, ex.Message);\n    }\n\n    static void AssertEvaluation(string expression, LogEvent match, params LogEvent[] noMatches)\n    {\n        var sink = new CollectingSink();\n\n        var log = new LoggerConfiguration()\n            .Filter.ByIncludingOnly(expression)\n            .WriteTo.Sink(sink)\n            .CreateLogger();\n\n        foreach (var noMatch in noMatches)\n            log.Write(noMatch);\n\n        log.Write(match);\n\n        Assert.Single(sink.Events);\n        Assert.Same(match, sink.Events.Single());\n    }\n}"
  },
  {
    "path": "test/Serilog.Expressions.Tests/ExpressionEvaluationTests.cs",
    "content": "using System.Diagnostics;\nusing System.Globalization;\nusing Serilog.Events;\nusing Serilog.Expressions.Runtime;\nusing Serilog.Expressions.Tests.Support;\nusing Serilog.Parsing;\nusing Xunit;\n\nnamespace Serilog.Expressions.Tests;\n\npublic class ExpressionEvaluationTests\n{\n    public static IEnumerable<object[]> ExpressionEvaluationCases =>\n        AsvCases.ReadCases(\"expression-evaluation-cases.asv\");\n\n    [Theory]\n    [MemberData(nameof(ExpressionEvaluationCases))]\n    public void ExpressionsAreCorrectlyEvaluated(string expr, string result)\n    {\n        var evt = new LogEvent(\n            new DateTimeOffset(2025, 5, 15, 13, 12, 11, 789, TimeSpan.FromHours(10)),\n            LogEventLevel.Warning,\n            new DivideByZeroException(),\n            new MessageTemplateParser().Parse(\"Hello, {Name}!\"),\n            new []\n            {\n                new LogEventProperty(\"Name\", new ScalarValue(\"World\")),\n                new LogEventProperty(\"User\", new StructureValue(new[]\n                {\n                    new LogEventProperty(\"Id\", new ScalarValue(42)),\n                    new LogEventProperty(\"Name\", new ScalarValue(\"nblumhardt\")),\n                }))\n            },\n            ActivityTraceId.CreateFromString(\"1befc31e94b01d1a473f63a7905f6c9b\"),\n            ActivitySpanId.CreateFromString(\"bb1111820570b80e\"));\n\n        var frFr = CultureInfo.GetCultureInfoByIetfLanguageTag(\"fr-FR\");\n        var testHelpers = new TestHelperNameResolver();\n        var actual = SerilogExpression.Compile(expr, formatProvider: frFr, testHelpers)(evt);\n        var expected = SerilogExpression.Compile(result, nameResolver: testHelpers)(evt);\n\n        if (expected is null)\n        {\n            Assert.True(actual is null, $\"Expected value: undefined{Environment.NewLine}Actual value: {Display(actual)}\");\n        }\n        else\n        {\n            Assert.True(Coerce.IsTrue(RuntimeOperators._Internal_Equal(StringComparison.OrdinalIgnoreCase, actual, expected)), $\"Expected value: {Display(expected)}{Environment.NewLine}Actual value: {Display(actual)}\");\n        }\n    }\n\n    static string Display(LogEventPropertyValue? value)\n    {\n        if (value == null)\n            return \"undefined\";\n\n        return value.ToString();\n    }\n}"
  },
  {
    "path": "test/Serilog.Expressions.Tests/ExpressionParserTests.cs",
    "content": "﻿using Serilog.Expressions.Parsing;\nusing Xunit;\n\nnamespace Serilog.Expressions.Tests;\n\npublic class ExpressionParserTests\n{\n    [Theory]\n    [InlineData(\"contains(@Message, 'some text')\", \"contains(@Message, 'some text')\")]\n    [InlineData(\"AProperty\", null)]\n    [InlineData(\"AProperty = 'some text'\", \"_Internal_Equal(AProperty, 'some text')\")]\n    [InlineData(\"AProperty = 42\", \"_Internal_Equal(AProperty, 42)\")]\n    [InlineData(\"@Properties['0'] = 42\", \"_Internal_Equal(@Properties['0'], 42)\")]\n    [InlineData(\"AProperty = null\", \"_Internal_Equal(AProperty, null)\")]\n    [InlineData(\"AProperty <> 42\", \"_Internal_NotEqual(AProperty, 42)\")]\n    [InlineData(\"has(AProperty)\", null)]\n    [InlineData(\"not A\", \"_Internal_Not(A)\")]\n    [InlineData(\"not (1 = 2)\", \"_Internal_Not(_Internal_Equal(1, 2))\")]\n    [InlineData(\"not(1 = 2)\", \"_Internal_Not(_Internal_Equal(1, 2))\")]\n    [InlineData(\"AProperty = 3 and Another < 12\", \"_Internal_And(_Internal_Equal(AProperty, 3), _Internal_LessThan(Another, 12))\")]\n    [InlineData(\"@Timestamp\", null)]\n    [InlineData(\"1 + (2 + 3) * 4\", \"_Internal_Add(1, _Internal_Multiply(_Internal_Add(2, 3), 4))\")]\n    [InlineData(\"AProperty.Another = 3\", \"_Internal_Equal(AProperty.Another, 3)\")]\n    [InlineData(\"AProperty /2/3 = 7\", \"_Internal_Equal(_Internal_Divide(_Internal_Divide(AProperty, 2), 3), 7)\")]\n    [InlineData(\"AProperty[0] = 1\", \"_Internal_Equal(AProperty[0], 1)\")]\n    [InlineData(\"AProperty[0] = note\", \"_Internal_Equal(AProperty[0], note)\")] // Ensure correct tokenization of 'not'\n    [InlineData(\"equal(AProperty[0].Description, 1)\", null)]\n    [InlineData(\"equal(AProperty[?].Description, 1)\", null)]\n    [InlineData(\"equal(AProperty[*].Description, 1)\", null)]\n    [InlineData(\"equal(AProperty[ * ].Description, 1)\", \"equal(AProperty[*].Description, 1)\")]\n    [InlineData(\"AProperty like '%foo'\", \"_Internal_Like(AProperty, '%foo')\")]\n    [InlineData(\"AProperty not like '%foo'\", \"_Internal_NotLike(AProperty, '%foo')\")]\n    [InlineData(\"A is null\", \"_Internal_IsNull(A)\")]\n    [InlineData(\"A IS NOT NULL\", \"_Internal_IsNotNull(A)\")]\n    [InlineData(\"A is not null or B\", \"_Internal_Or(_Internal_IsNotNull(A), B)\")]\n    [InlineData(\"@EventType = 0xC0ffee\", \"_Internal_Equal(@EventType, 12648430)\")]\n    [InlineData(\"@Level in ['Error', 'Warning']\", \"_Internal_In(@Level, ['Error', 'Warning'])\")]\n    [InlineData(\"5 not in [1, 2]\", \"_Internal_NotIn(5, [1, 2])\")]\n    [InlineData(\"1+1\", \"_Internal_Add(1, 1)\")]\n    [InlineData(\"'te\\nst'\", null)]\n    [InlineData(\"A.B is null\", \"_Internal_IsNull(A.B)\")]\n    public void ValidSyntaxIsAccepted(string input, string? expected = null)\n    {\n        var expressionParser = new ExpressionParser();\n        var roundTrip = expressionParser.Parse(input).ToString();\n        Assert.Equal(expected ?? input, roundTrip);\n    }\n\n    [Theory]\n    [InlineData(\" \")]\n    [InlineData(\"\\\"Hello!\\\"\")]\n    [InlineData(\"@\\\"Hello!\\\"\")]\n    [InlineData(\"$FE6789E6\")]\n    [InlineData(\"AProperty == 'some text'\")]\n    [InlineData(\"AProperty != 42\")]\n    [InlineData(\"!(1 == 2)\")]\n    [InlineData(\"AProperty = 3 && Another < 12\")]\n    [InlineData(\"A = 99999999999999999999999999999999999999999999\")]\n    [InlineData(\"A = 0x99999999999999999999999999999999999999999999\")]\n    public void InvalidSyntaxIsRejected(string input)\n    {\n        var expressionParser = new ExpressionParser();\n        Assert.Throws<ArgumentException>(() => expressionParser.Parse(input));\n    }\n\n    [Theory]\n    [InlineData(\"A = 'b\", \"Syntax error: unexpected end of input, expected `'`.\")]\n    [InlineData(\"A or B) and C\", \"Syntax error (line 1, column 7): unexpected `)`.\")]\n    [InlineData(\"A lik3 C\", \"Syntax error (line 1, column 3): unexpected identifier `lik3`.\")]\n    [InlineData(\"A > 1234f\", \"Syntax error (line 1, column 9): unexpected `f`, expected digit.\")]\n    public void PreciseErrorsAreReported(string input, string expectedMessage)\n    {\n        var expressionParser = new ExpressionParser();\n        var ex = Assert.Throws<ArgumentException>(() => expressionParser.Parse(input));\n        Assert.Equal(expectedMessage, ex.Message);\n    }\n}"
  },
  {
    "path": "test/Serilog.Expressions.Tests/ExpressionTranslationTests.cs",
    "content": "using Serilog.Expressions.Compilation;\nusing Serilog.Expressions.Parsing;\nusing Serilog.Expressions.Tests.Support;\nusing Xunit;\n\nnamespace Serilog.Expressions.Tests;\n\npublic class ExpressionTranslationTests\n{\n    public static IEnumerable<object[]> ExpressionTranslationCases =>\n        AsvCases.ReadCases(\"translation-cases.asv\");\n\n    [Theory]\n    [MemberData(nameof(ExpressionTranslationCases))]\n    public void ExpressionsAreCorrectlyTranslated(string expr, string expected)\n    {\n        var parsed = new ExpressionParser().Parse(expr);\n        var translated = ExpressionCompiler.Translate(parsed);\n        var actual = translated.ToString();\n        Assert.Equal(expected, actual);\n    }\n}"
  },
  {
    "path": "test/Serilog.Expressions.Tests/ExpressionValidationTests.cs",
    "content": "using Xunit;\n\nnamespace Serilog.Expressions.Tests;\n\npublic class ExpressionValidationTests\n{\n    [Theory]\n    [InlineData(\"IsMatch(Name, '[invalid')\", \"Invalid regular expression\")]\n    [InlineData(\"IndexOfMatch(Text, '(?<')\", \"Invalid regular expression\")]\n    [InlineData(\"IsMatch(Name, '(?P<name>)')\", \"Invalid regular expression\")]\n    [InlineData(\"IsMatch(Name, '(unclosed')\", \"Invalid regular expression\")]\n    [InlineData(\"IndexOfMatch(Text, '*invalid')\", \"Invalid regular expression\")]\n    public void InvalidRegularExpressionsAreReportedGracefully(string expression, string expectedErrorFragment)\n    {\n        var result = SerilogExpression.TryCompile(expression, out var compiled, out var error);\n        Assert.False(result);\n        Assert.Contains(expectedErrorFragment, error);\n        Assert.Null(compiled);\n    }\n\n    [Theory]\n    [InlineData(\"UnknownFunction()\", \"The function name `UnknownFunction` was not recognized.\")]\n    [InlineData(\"foo(1, 2, 3)\", \"The function name `foo` was not recognized.\")]\n    [InlineData(\"MyCustomFunc(Name)\", \"The function name `MyCustomFunc` was not recognized.\")]\n    [InlineData(\"notAFunction()\", \"The function name `notAFunction` was not recognized.\")]\n    public void UnknownFunctionNamesAreReportedGracefully(string expression, string expectedError)\n    {\n        var result = SerilogExpression.TryCompile(expression, out var compiled, out var error);\n        Assert.False(result);\n        Assert.Equal(expectedError, error);\n        Assert.Null(compiled);\n    }\n\n    [Theory]\n    [InlineData(\"Length(Name) ci\")]\n    [InlineData(\"Round(Value, 2) ci\")]\n    [InlineData(\"Now() ci\")]\n    [InlineData(\"TypeOf(Value) ci\")]\n    [InlineData(\"IsDefined(Prop) ci\")]\n    public void InvalidCiModifierUsageCompilesWithWarning(string expression)\n    {\n        var result = SerilogExpression.TryCompile(expression, out var compiled, out var error);\n        Assert.True(result, $\"Failed to compile: {error}\");\n        Assert.NotNull(compiled);\n        Assert.Null(error);\n    }\n\n    [Theory]\n    [InlineData(\"Contains(Name, 'test') ci\")]\n    [InlineData(\"StartsWith(Path, '/api') ci\")]\n    [InlineData(\"EndsWith(File, '.txt') ci\")]\n    [InlineData(\"IsMatch(Email, '@example') ci\")]\n    [InlineData(\"IndexOfMatch(Text, 'pattern') ci\")]\n    [InlineData(\"IndexOf(Name, 'x') ci\")]\n    [InlineData(\"Name = 'test' ci\")]\n    [InlineData(\"Name <> 'test' ci\")]\n    [InlineData(\"Name like '%test%' ci\")]\n    public void ValidCiModifierUsageCompiles(string expression)\n    {\n        var result = SerilogExpression.TryCompile(expression, out var compiled, out var error);\n        Assert.True(result, $\"Failed to compile: {error}\");\n        Assert.NotNull(compiled);\n        Assert.Null(error);\n    }\n\n    [Fact]\n    public void FirstErrorIsReportedInComplexExpressions()\n    {\n        var expression = \"UnknownFunc() and Length(Value) > 5\";\n        var result = SerilogExpression.TryCompile(expression, out var compiled, out var error);\n        \n        Assert.False(result);\n        Assert.Null(compiled);\n        \n        // Should report the first error encountered\n        Assert.Contains(\"UnknownFunc\", error);\n        Assert.NotNull(error);\n    }\n\n    [Fact]\n    public void ValidExpressionsStillCompileWithoutErrors()\n    {\n        var validExpressions = new[]\n        {\n            \"IsMatch(Name, '^[A-Z]')\",\n            \"IndexOfMatch(Text, '\\\\d+')\",\n            \"Contains(Name, 'test') ci\",\n            \"Length(Items) > 5\",\n            \"Round(Value, 2)\",\n            \"TypeOf(Data) = 'String'\",\n            \"Name like '%test%'\",\n            \"StartsWith(Path, '/') and EndsWith(Path, '.json')\"\n        };\n        \n        foreach (var expr in validExpressions)\n        {\n            var result = SerilogExpression.TryCompile(expr, out var compiled, out var error);\n            Assert.True(result, $\"Failed to compile: {expr}. Error: {error}\");\n            Assert.NotNull(compiled);\n            Assert.Null(error);\n        }\n    }\n\n    [Fact]\n    public void CompileMethodStillThrowsForInvalidExpressions()\n    {\n        // Ensure Compile() method (not TryCompile) maintains throwing behavior for invalid expressions\n        Assert.Throws<ArgumentException>(() => \n            SerilogExpression.Compile(\"UnknownFunction()\"));\n        \n        Assert.Throws<ArgumentException>(() => \n            SerilogExpression.Compile(\"IsMatch(Name, '[invalid')\"));\n        \n        // CI modifier on non-supporting functions compiles with warning\n        var compiledWithCi = SerilogExpression.Compile(\"Length(Name) ci\");\n        Assert.NotNull(compiledWithCi);\n        \n        Assert.Throws<ArgumentException>(() => \n            SerilogExpression.Compile(\"IndexOfMatch(Text, '(?<')\"));\n    }\n\n    [Theory]\n    [InlineData(\"IsMatch(Name, Name)\")] // Non-constant pattern\n    [InlineData(\"IndexOfMatch(Text, Value)\")] // Non-constant pattern\n    public void NonConstantRegexPatternsHandledGracefully(string expression)\n    {\n        // These should compile but may log warnings (not errors)\n        var result = SerilogExpression.TryCompile(expression, out var compiled, out var error);\n        \n        // These compile successfully but return undefined at runtime\n        Assert.True(result);\n        Assert.NotNull(compiled);\n        Assert.Null(error);\n    }\n\n    [Fact]\n    public void RegexTimeoutIsRespected()\n    {\n        // This regex should compile fine - timeout only matters at runtime\n        var expression = @\"IsMatch(Text, '(a+)+b')\";\n        \n        var result = SerilogExpression.TryCompile(expression, out var compiled, out var error);\n        \n        Assert.True(result);\n        Assert.NotNull(compiled);\n        Assert.Null(error);\n    }\n\n    [Fact]\n    public void ComplexExpressionsReportFirstError()\n    {\n        var expression = \"UnknownFunc1() or Length(Value) > 5\";\n        var result = SerilogExpression.TryCompile(expression, out var compiled, out var error);\n        \n        Assert.False(result);\n        Assert.Null(compiled);\n        Assert.NotNull(error);\n        \n        // Should report the first error encountered during compilation\n        Assert.Contains(\"UnknownFunc1\", error);\n    }\n\n    [Fact]\n    public void BackwardCompatibilityPreservedForInvalidCiUsage()\n    {\n        // These previously compiled (CI was silently ignored)\n        // They should still compile with the new changes\n        var expressions = new[]\n        {\n            \"undefined() ci\",\n            \"null = undefined() ci\",\n            \"Length(Name) ci\",\n            \"Round(Value, 2) ci\"\n        };\n\n        foreach (var expr in expressions)\n        {\n            var result = SerilogExpression.TryCompile(expr, out var compiled, out var error);\n            Assert.True(result, $\"Breaking change detected: {expr} no longer compiles. Error: {error}\");\n            Assert.NotNull(compiled);\n            Assert.Null(error);\n        }\n    }\n}"
  },
  {
    "path": "test/Serilog.Expressions.Tests/ExpressionValueTests.cs",
    "content": "﻿using Serilog.Events;\nusing Xunit;\n\nnamespace Serilog.Expressions.Tests;\n\npublic class ExpressionValueTests\n{\n    [Fact]\n    public void UndefinedResultsAreFalse()\n    {\n        Assert.False(ExpressionResult.IsTrue(null));\n    }\n\n    [Fact]\n    public void NonBooleanResultsAreFalse()\n    {\n        Assert.False(ExpressionResult.IsTrue(new ScalarValue(10)));\n    }\n\n    [Fact]\n    public void TrueIsTrue()\n    {\n        Assert.True(ExpressionResult.IsTrue(new ScalarValue(true)));\n    }\n\n    [Fact]\n    public void FalseIsNotTrue()\n    {\n        Assert.False(ExpressionResult.IsTrue(new ScalarValue(false)));\n    }\n}"
  },
  {
    "path": "test/Serilog.Expressions.Tests/Expressions/NameResolverTests.cs",
    "content": "﻿using System.Diagnostics.CodeAnalysis;\nusing System.Reflection;\nusing Serilog.Events;\nusing Serilog.Expressions.Runtime;\nusing Serilog.Expressions.Tests.Support;\nusing Xunit;\n\nnamespace Serilog.Expressions.Tests.Expressions;\n\npublic class NameResolverTests\n{\n    // ReSharper disable once UnusedMember.Global\n    public static LogEventPropertyValue? Magic(LogEventPropertyValue? number)\n    {\n        if (!Coerce.Numeric(number, out var num))\n            return null;\n\n        return new ScalarValue(num + 42);\n    }\n\n    // ReSharper disable once UnusedMember.Global\n    public static LogEventPropertyValue? SecretWordAt(string word, LogEventPropertyValue? index)\n    {\n        if (!Coerce.Numeric(index, out var i))\n            return null;\n\n        return new ScalarValue(word[(int)i].ToString());\n    }\n\n    class SecretWordResolver : NameResolver\n    {\n        readonly NameResolver _inner;\n        readonly string _word;\n\n        public SecretWordResolver(NameResolver inner, string word)\n        {\n            _inner = inner;\n            _word = word;\n        }\n\n        public override bool TryResolveFunctionName(string name, [MaybeNullWhen(false)] out MethodInfo implementation)\n            => _inner.TryResolveFunctionName(name, out implementation);\n\n        public override bool TryBindFunctionParameter(ParameterInfo parameter, [MaybeNullWhen(false)] out object boundValue)\n        {\n            if (parameter.ParameterType == typeof(string))\n            {\n                boundValue = _word;\n                return true;\n            }\n\n            boundValue = null;\n            return false;\n        }\n    }\n\n    class LegacyLevelPropertyNameResolver: NameResolver\n    {\n        public override bool TryResolveBuiltInPropertyName(string alias, [NotNullWhen(true)] out string? target)\n        {\n            if (alias == \"Level\")\n            {\n                target = \"l\";\n                return true;\n            }\n\n            target = null;\n            return false;\n        }\n    }\n\n    [Fact]\n    public void UserDefinedFunctionsAreCallableInExpressions()\n    {\n        var expr = SerilogExpression.Compile(\n            \"magic(10) + 3 = 55\",\n            nameResolver: new StaticMemberNameResolver(typeof(NameResolverTests)));\n        Assert.True(Coerce.IsTrue(expr(Some.InformationEvent())));\n    }\n\n    [Fact]\n    public void UserDefinedFunctionsCanReceiveUserProvidedParameters()\n    {\n        var expr = SerilogExpression.Compile(\n            \"SecretWordAt(1) = 'e'\",\n            nameResolver: new SecretWordResolver(new StaticMemberNameResolver(typeof(NameResolverTests)), \"hello\"));\n        Assert.True(Coerce.IsTrue(expr(Some.InformationEvent())));\n    }\n\n    [Fact]\n    public void BuiltInPropertiesCanBeAliased()\n    {\n        var expr = SerilogExpression.Compile(\n            \"@Level = 'Information'\",\n            nameResolver: new LegacyLevelPropertyNameResolver());\n        Assert.True(Coerce.IsTrue(expr(Some.InformationEvent())));\n    }\n}"
  },
  {
    "path": "test/Serilog.Expressions.Tests/Expressions/Runtime/LocalsTests.cs",
    "content": "﻿using Serilog.Expressions.Runtime;\nusing Serilog.Expressions.Tests.Support;\nusing Xunit;\n\nnamespace Serilog.Expressions.Tests.Expressions.Runtime;\n\npublic class LocalsTests\n{\n    [Fact]\n    public void NoValueIsDefinedInNoLocals()\n    {\n        Assert.False(Locals.TryGetValue(null, \"A\", out _));\n    }\n\n    [Fact]\n    public void ASetValueIsRetrieved()\n    {\n        var expected = Some.LogEventPropertyValue();\n        var locals = Locals.Set(null, \"A\", expected);\n        Assert.True(Locals.TryGetValue(locals, \"A\", out var actual));\n        Assert.Same(expected, actual);\n    }\n\n    [Fact]\n    public void ASetValueIsRetrievedFromMany()\n    {\n        var expected = Some.LogEventPropertyValue();\n        var locals = Locals.Set(null, \"A\", expected);\n        locals = Locals.Set(locals, \"B\", Some.LogEventPropertyValue());\n        Assert.True(Locals.TryGetValue(locals, \"A\", out var actual));\n        Assert.Same(expected, actual);\n    }\n\n    [Fact]\n    public void TheTopmostValueIsRetrievedForAName()\n    {\n        var expected = Some.LogEventPropertyValue();\n        var locals = Locals.Set(null, \"A\", Some.LogEventPropertyValue());\n        locals = Locals.Set(locals, \"B\", Some.LogEventPropertyValue());\n        locals = Locals.Set(locals, \"A\", expected);\n        Assert.True(Locals.TryGetValue(locals, \"A\", out var actual));\n        Assert.Same(expected, actual);\n    }\n}"
  },
  {
    "path": "test/Serilog.Expressions.Tests/Expressions/Runtime/RuntimeOperatorsTests.cs",
    "content": "using System.Reflection;\nusing Serilog.Events;\nusing Serilog.Expressions.Runtime;\nusing Serilog.Expressions.Tests.Support;\nusing Xunit;\n\nnamespace Serilog.Expressions.Tests.Expressions.Runtime;\n\npublic class RuntimeOperatorsTests\n{\n    [Fact]\n    public void InspectReadsPublicPropertiesFromScalarValue()\n    {\n        var message = Some.String();\n        var ex = new DivideByZeroException(message);\n        var scalar = new ScalarValue(ex);\n        var inspected = RuntimeOperators.Inspect(scalar);\n        var structure = Assert.IsType<StructureValue>(inspected);\n        var asProperties = structure.Properties.ToDictionary(p => p.Name, p => p.Value);\n        Assert.Contains(\"Message\", asProperties);\n        Assert.Contains(\"StackTrace\", asProperties);\n        var messageResult = Assert.IsType<ScalarValue>(asProperties[\"Message\"]);\n        Assert.Equal(message, messageResult.Value);\n    }\n\n    [Fact]\n    public void DeepInspectionReadsSubproperties()\n    {\n        var innerMessage = Some.String();\n        var inner = new DivideByZeroException(innerMessage);\n        var ex = new TargetInvocationException(inner);\n        var scalar = new ScalarValue(ex);\n        var inspected = RuntimeOperators.Inspect(scalar, deep: new ScalarValue(true));\n        var structure = Assert.IsType<StructureValue>(inspected);\n        var innerStructure = Assert.IsType<StructureValue>(structure.Properties.Single(p => p.Name == \"InnerException\").Value);\n        var innerMessageValue = Assert.IsType<ScalarValue>(innerStructure.Properties.Single(p => p.Name == \"Message\").Value);\n        Assert.Equal(innerMessage, innerMessageValue.Value);\n    }\n}"
  },
  {
    "path": "test/Serilog.Expressions.Tests/FormatParityTests.cs",
    "content": "﻿using Serilog.Events;\nusing Serilog.Expressions.Tests.Support;\nusing Serilog.Formatting;\nusing Serilog.Formatting.Compact;\nusing Serilog.Formatting.Json;\nusing Serilog.Parsing;\nusing Serilog.Templates;\nusing Xunit;\n\nnamespace Serilog.Expressions.Tests;\n\n/// <summary>\n/// These tests track the ability of Serilog.Expressions to faithfully reproduce the JSON formats implemented in\n/// Serilog and Serilog.Formatting.Compact. The tests jump through a few hoops to achieve byte-for-byte correctness;\n/// in practice, valid JSON in these formats can be constructed with simpler templates.\n/// </summary>\npublic class FormatParityTests\n{\n    // Implements CLEF-style `@@` escaping of property names that begin with `@`.\n    // ReSharper disable once UnusedMember.Global\n    public static LogEventPropertyValue? ClefEscape(LogEventPropertyValue? logEventProperties)\n    {\n        if (logEventProperties is not StructureValue st)\n            return null;\n\n        foreach (var check in st.Properties)\n        {\n            if (check.Name.Length > 0 && check.Name[0] == '@')\n            {\n                var properties = new List<LogEventProperty>();\n\n                foreach (var member in st.Properties)\n                {\n                    var property = new LogEventProperty(\n                        member.Name.Length > 0 && member.Name[0] == '@' ? \"@\" + member.Name : member.Name,\n                        member.Value);\n\n                    properties.Add(property);\n                }\n\n                return new StructureValue(properties, st.TypeTag);\n            }\n        }\n\n        return logEventProperties;\n    }\n\n    // Renders a message template with old-style \"quoted\" strings (expression templates use the newer :lj formatting always).\n    // ReSharper disable once UnusedMember.Global\n    public static LogEventPropertyValue? ClassicRender(LogEventPropertyValue? messageTemplate, LogEventPropertyValue? properties)\n    {\n        if (messageTemplate is not ScalarValue {Value: string smt} ||\n            properties is not StructureValue stp)\n        {\n            return null;\n        }\n\n        var mt = new MessageTemplateParser().Parse(smt);\n        var space = new StringWriter();\n        mt.Render(stp.Properties.ToDictionary(p => p.Name, p => p.Value), space);\n        return new ScalarValue(space.ToString());\n    }\n\n    // Constructs the Renderings property used in the old JSON format.\n    // ReSharper disable once UnusedMember.Global\n    public static LogEventPropertyValue? ClassicRenderings(LogEventPropertyValue? messageTemplate, LogEventPropertyValue? properties)\n    {\n        if (messageTemplate is not ScalarValue {Value: string smt} ||\n            properties is not StructureValue stp)\n        {\n            return null;\n        }\n\n        var mt = new MessageTemplateParser().Parse(smt);\n        var tokensWithFormat = mt.Tokens\n            .OfType<PropertyToken>()\n            .Where(pt => pt.Format != null)\n            .GroupBy(pt => pt.PropertyName);\n\n        // ReSharper disable once PossibleMultipleEnumeration\n        if (!tokensWithFormat.Any())\n            return null;\n\n        var propertiesByName = stp.Properties.ToDictionary(p => p.Name, p => p.Value);\n\n        var renderings = new List<LogEventProperty>();\n        // ReSharper disable once PossibleMultipleEnumeration\n        foreach (var propertyFormats in tokensWithFormat)\n        {\n            var values = new List<LogEventPropertyValue>();\n\n            foreach (var format in propertyFormats)\n            {\n                var sw = new StringWriter();\n\n                format.Render(propertiesByName, sw);\n\n                values.Add(new StructureValue(new []\n                {\n                    new LogEventProperty(\"Format\", new ScalarValue(format.Format)),\n                    new LogEventProperty(\"Rendering\", new ScalarValue(sw.ToString())),\n                }));\n            }\n\n            renderings.Add(new(propertyFormats.Key, new SequenceValue(values)));\n        }\n\n        return new StructureValue(renderings);\n    }\n\n    readonly ITextFormatter\n        _clef = new CompactJsonFormatter(),\n        _renderedClef = new RenderedCompactJsonFormatter(),\n        _classic = new JsonFormatter(),\n        _clefExpression = new ExpressionTemplate(\n            \"{ {@t: UtcDateTime(@t), @mt, @r, @l: if @l = 'Information' then undefined() else @l, @x, ..ClefEscape(@p)} }\" + Environment.NewLine,\n            null, new StaticMemberNameResolver(typeof(FormatParityTests))),\n        _renderedClefExpression = new ExpressionTemplate(\n            \"{ {@t: UtcDateTime(@t), @m: ClassicRender(@mt, @p), @i: ToString(@i, 'x8'), @l: if @l = 'Information' then undefined() else @l, @x, ..ClefEscape(@p)} }\" + Environment.NewLine,\n            null, new StaticMemberNameResolver(typeof(FormatParityTests))),\n        _classicExpression = new ExpressionTemplate(\n            \"{ {Timestamp: @t, Level: @l, MessageTemplate: @mt, Exception: @x, Properties: if IsDefined(@p[?]) then @p else undefined(), Renderings: ClassicRenderings(@mt, @p)} }\" + Environment.NewLine,\n            null, new StaticMemberNameResolver(typeof(FormatParityTests)));\n\n    static string Render(\n        ITextFormatter formatter,\n        LogEvent logEvent)\n    {\n        var space = new StringWriter();\n        formatter.Format(logEvent, space);\n        return space.ToString();\n    }\n\n    void AssertWriteParity(\n        LogEventLevel level,\n        Exception? exception,\n        string messageTemplate,\n        params object[] propertyValues)\n    {\n        var sink = new CollectingSink();\n        using (var log = new LoggerConfiguration()\n                   .MinimumLevel.Is(LevelAlias.Minimum)\n                   .WriteTo.Sink(sink)\n                   .CreateLogger())\n        {\n            log.Write(level, exception, messageTemplate, propertyValues);\n        }\n\n        var clef = Render(_clef, sink.SingleEvent);\n        var clefExpression = Render(_clefExpression, sink.SingleEvent);\n        Assert.Equal(clef, clefExpression);\n\n        var renderedClef = Render(_renderedClef, sink.SingleEvent);\n        var renderedClefExpression = Render(_renderedClefExpression, sink.SingleEvent);\n        Assert.Equal(renderedClef, renderedClefExpression);\n\n        var renderedClassic = Render(_classic, sink.SingleEvent);\n        var renderedClassicExpression = Render(_classicExpression, sink.SingleEvent);\n        Assert.Equal(renderedClassic, renderedClassicExpression);\n    }\n\n    [Fact]\n    public void ParityIsMaintained()\n    {\n        AssertWriteParity(LogEventLevel.Information, null, \"Hello, world!\");\n        AssertWriteParity(LogEventLevel.Debug, new(), \"Hello, {Name}, {Number:000}, {Another:#.00}\", \"world\", 42, 3.1);\n    }\n}"
  },
  {
    "path": "test/Serilog.Expressions.Tests/LoggingFilterSwitchTests.cs",
    "content": "﻿using Serilog.Expressions.Tests.Support;\nusing Xunit;\n\nnamespace Serilog.Expressions.Tests;\n\npublic class LoggingFilterSwitchTests\n{\n    [Fact]\n    public void WhenTheFilterExpressionIsModifiedTheFilterChanges()\n    {\n        var @switch = new LoggingFilterSwitch();\n        var sink = new CollectingSink();\n\n        var log = new LoggerConfiguration()\n            .Filter.ControlledBy(@switch)\n            .WriteTo.Sink(sink)\n            .CreateLogger();\n\n        var v11 = Some.InformationEvent(\"Adding {Volume} L\", 11);\n\n        log.Write(v11);\n        Assert.Same(v11, sink.SingleEvent);\n        sink.Events.Clear();\n\n        @switch.Expression = \"Volume > 12\";\n\n        log.Write(v11);\n        Assert.Empty(sink.Events);\n\n        @switch.Expression = \"Volume > 10\";\n\n        log.Write(v11);\n        Assert.Same(v11, sink.SingleEvent);\n        sink.Events.Clear();\n\n        @switch.Expression = null;\n\n        log.Write(v11);\n        Assert.Same(v11, sink.SingleEvent);\n    }\n}"
  },
  {
    "path": "test/Serilog.Expressions.Tests/Properties/launchSettings.json",
    "content": "{\n  \"profiles\": {\n    \"test\": {\n      \"commandName\": \"test\"\n    },\n    \"test-dnxcore50\": {\n      \"commandName\": \"test\",\n      \"sdkVersion\": \"dnx-coreclr-win-x86.1.0.0-rc1-final\"\n    }\n  }\n}"
  },
  {
    "path": "test/Serilog.Expressions.Tests/Serilog.Expressions.Tests.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <TargetFramework>net9.0</TargetFramework>\n    <GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>\n    <GenerateDocumentationFile>false</GenerateDocumentationFile>\n  </PropertyGroup>\n  <ItemGroup>\n    <PackageReference Include=\"Microsoft.NET.Test.Sdk\" Version=\"17.13.0\" />\n    <PackageReference Include=\"xunit.runner.visualstudio\" Version=\"3.0.2\">\n      <PrivateAssets>all</PrivateAssets>\n      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>\n    </PackageReference>\n    <PackageReference Include=\"xunit\" Version=\"2.9.3\" />\n    <PackageReference Include=\"Serilog.Formatting.Compact\" Version=\"3.0.0\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"../../src/Serilog.Expressions/Serilog.Expressions.csproj\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Content Include=\"Cases/*.asv\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "test/Serilog.Expressions.Tests/Support/AsvCases.cs",
    "content": "﻿namespace Serilog.Expressions.Tests.Support;\n\n// \"Arrow-separated values ;-) ... convenient because the Unicode `⇶` character doesn't appear in\n// any of the cases themselves, and so we ignore any need for special character escaping (which is\n// troublesome when the language the cases are written in uses just about all special characters somehow\n// or other!).\n//\n// The ASV format informally supports `//` comment lines, as long as they don't contain the arrow character.\nstatic class AsvCases\n{\n    static readonly string CasesPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory!, \"Cases\");\n\n    public static IEnumerable<object[]> ReadCases(string filename)\n    {\n        return from line in File.ReadLines(Path.Combine(CasesPath, filename))\n            select line.Split(\"⇶\", StringSplitOptions.RemoveEmptyEntries) into cols\n            where cols.Length == 2\n            select cols.Select(c => c.Trim()).ToArray<object>();\n    }\n}"
  },
  {
    "path": "test/Serilog.Expressions.Tests/Support/CollectingSink.cs",
    "content": "﻿using Serilog.Core;\nusing Serilog.Events;\n\nnamespace Serilog.Expressions.Tests.Support;\n\nclass CollectingSink : ILogEventSink\n{\n    readonly List<LogEvent> _events = new();\n\n    public List<LogEvent> Events { get { return _events; } }\n\n    public LogEvent SingleEvent { get { return _events.Single(); } }\n\n    public void Emit(LogEvent logEvent)\n    {\n        _events.Add(logEvent);\n    }\n}"
  },
  {
    "path": "test/Serilog.Expressions.Tests/Support/ParenthesizingEncoder.cs",
    "content": "﻿using Serilog.Templates.Encoding;\n\nnamespace Serilog.Expressions.Tests.Support;\n\npublic class ParenthesizingEncoder : TemplateOutputEncoder\n{\n    public override string Encode(string value)\n    {\n        return $\"({value})\";\n    }\n}\n"
  },
  {
    "path": "test/Serilog.Expressions.Tests/Support/Some.cs",
    "content": "﻿using Serilog.Events;\nusing Xunit.Sdk;\n\nnamespace Serilog.Expressions.Tests.Support;\n\nstatic class Some\n{\n    static int _next;\n    \n    public static LogEvent InformationEvent(string messageTemplate = \"Hello, world!\", params object?[] propertyValues)\n    {\n        return LogEvent(LogEventLevel.Information, messageTemplate, propertyValues);\n    }\n\n    public static LogEvent InformationEvent(DateTimeOffset timestamp, string messageTemplate = \"Hello, world!\", params object?[] propertyValues)\n    {\n        return LogEvent(timestamp, LogEventLevel.Information, messageTemplate, propertyValues);\n    }\n\n    public static LogEvent WarningEvent(string messageTemplate = \"Hello, world!\", params object?[] propertyValues)\n    {\n        return LogEvent(LogEventLevel.Warning, messageTemplate, propertyValues);\n    }\n\n    public static LogEvent LogEvent(LogEventLevel level, string messageTemplate = \"Hello, world!\", params object?[] propertyValues)\n    {\n        return LogEvent(DateTimeOffset.Now, level, messageTemplate, propertyValues);\n    }\n\n    public static LogEvent LogEvent(DateTimeOffset timestamp, LogEventLevel level, string messageTemplate = \"Hello, world!\", params object?[] propertyValues)\n    {\n        var log = new LoggerConfiguration().CreateLogger();\n#pragma warning disable Serilog004 // Constant MessageTemplate verifier\n        if (!log.BindMessageTemplate(messageTemplate, propertyValues, out var template, out var properties))\n#pragma warning restore Serilog004 // Constant MessageTemplate verifier\n        {\n            throw new XunitException(\"Template could not be bound.\");\n        }\n        return new(timestamp, level, null, template, properties);\n    }\n\n    public static object AnonymousObject()\n    {\n        return new {A = Int()};\n    }\n\n    public static LogEventPropertyValue LogEventPropertyValue()\n    {\n        return new ScalarValue(AnonymousObject());\n    }\n\n    static int Int()\n    {\n        return Interlocked.Increment(ref _next);\n    }\n\n    public static string String()\n    {\n        return $\"+S_{Int()}\";\n    }\n}\n"
  },
  {
    "path": "test/Serilog.Expressions.Tests/Support/StringHashPrefixingTheme.cs",
    "content": "using Serilog.Templates.Themes;\n\nnamespace Serilog.Expressions.Tests.Support;\n\nstatic class StringHashPrefixingTheme\n{\n    public static readonly TemplateTheme Instance = new(new Dictionary<TemplateThemeStyle, string>\n    {\n        [TemplateThemeStyle.String] = \"#\"\n    });\n}"
  },
  {
    "path": "test/Serilog.Expressions.Tests/Support/TestHelperNameResolver.cs",
    "content": "using System.Diagnostics.CodeAnalysis;\nusing System.Reflection;\nusing Serilog.Events;\n\nnamespace Serilog.Expressions.Tests.Support;\n\npublic class TestHelperNameResolver: NameResolver\n{\n    public override bool TryResolveFunctionName(string name, [MaybeNullWhen(false)] out MethodInfo implementation)\n    {\n        if (name == \"test_dict\")\n        {\n            implementation = GetType().GetMethod(nameof(TestDict))!;\n            return true;\n        }\n\n        implementation = null;\n        return false;\n    }\n\n    public static LogEventPropertyValue? TestDict(LogEventPropertyValue? value)\n    {\n        if (value is not StructureValue sv)\n            return null;\n\n        return new DictionaryValue(sv.Properties.Select(kv =>\n            KeyValuePair.Create(new ScalarValue(kv.Name), kv.Value)));\n    }\n}\n"
  },
  {
    "path": "test/Serilog.Expressions.Tests/TemplateEncodingTests.cs",
    "content": "using Serilog.Expressions.Tests.Support;\nusing Serilog.Templates;\nusing Xunit;\n\nnamespace Serilog.Expressions.Tests;\n\npublic class TemplateEncodingTests\n{\n    public static IEnumerable<object[]> TemplateEvaluationCases =>\n        AsvCases.ReadCases(\"template-encoding-cases.asv\");\n\n    [Theory]\n    [MemberData(nameof(TemplateEvaluationCases))]\n    public void TemplatesAreCorrectlyEvaluated(string template, string expected)\n    {\n        var evt = Some.InformationEvent(\"Hello, {Name}!\", \"nblumhardt\");\n        var compiled = new ExpressionTemplate(template, encoder: new ParenthesizingEncoder());\n        var output = new StringWriter();\n        compiled.Format(evt, output);\n        var actual = output.ToString();\n        Assert.Equal(expected, actual);\n    }\n\n    // Either theme or encoding must be applied first; although it's possible to imagine future scenarios (themes as HTML elements with styles...) that\n    // might combine these both in a more sophisticated way, the current implementation chooses to wrap the entire output in the encoder, instead\n    // of the other way around, because it's much easier to exclude any possibility of missed encoding using this approach.\n    [Fact]\n    public void EncodingAppliesToThemedOutput()\n    {\n        var evt = Some.InformationEvent(\"Hello, {Name}!\", \"nblumhardt\");\n        \n        var compiled = new ExpressionTemplate(\"-{@m}-\",\n            theme: StringHashPrefixingTheme.Instance,\n            encoder: new ParenthesizingEncoder(),\n            applyThemeWhenOutputIsRedirected: true);\n        \n        var output = new StringWriter();\n        compiled.Format(evt, output);\n        var actual = output.ToString();\n        Assert.Equal(\"-(Hello, #nblumhardt\\x1b[0m!)-\", actual);\n    }\n}"
  },
  {
    "path": "test/Serilog.Expressions.Tests/TemplateEvaluationTests.cs",
    "content": "using System.Globalization;\nusing Serilog.Events;\nusing Serilog.Expressions.Tests.Support;\nusing Serilog.Templates;\nusing Xunit;\n\nnamespace Serilog.Expressions.Tests;\n\npublic class TemplateEvaluationTests\n{\n    static readonly DateTimeOffset TestTimestamp = new(\n        2000, 12, 31, 23, 59, 58, 123, TimeSpan.FromHours(10));\n\n    public static IEnumerable<object[]> TemplateEvaluationCases =>\n        AsvCases.ReadCases(\"template-evaluation-cases.asv\");\n\n    [Theory]\n    [MemberData(nameof(TemplateEvaluationCases))]\n    public void TemplatesAreCorrectlyEvaluated(string template, string expected)\n    {\n        var evt = Some.InformationEvent(TestTimestamp, \"Hello, {Name}!\", \"nblumhardt\");\n        var frFr = CultureInfo.GetCultureInfoByIetfLanguageTag(\"fr-FR\");\n        var compiled = new ExpressionTemplate(template, formatProvider: frFr);\n        var output = new StringWriter();\n        compiled.Format(evt, output);\n        var actual = output.ToString();\n        Assert.Equal(expected, actual);\n    }\n}"
  },
  {
    "path": "test/Serilog.Expressions.Tests/TemplateParserTests.cs",
    "content": "﻿using Serilog.Templates;\nusing Serilog.Templates.Ast;\nusing Serilog.Templates.Parsing;\nusing Xunit;\n\nnamespace Serilog.Expressions.Tests;\n\npublic class TemplateParserTests\n{\n    [Theory]\n    [InlineData(\"Trailing {\", \"Syntax error: unexpected end of input, expected expression.\")]\n    [InlineData(\"Lonely } bracket\", \"Syntax error (line 1, column 9): unexpected space, expected escaped `}`.\")]\n    [InlineData(\"Trailing }\", \"Syntax error: unexpected end of input, expected escaped `}`.\")]\n    [InlineData(\"Unclosed {hole\", \"Syntax error: unexpected end of input, expected `}`.\")]\n    [InlineData(\"Syntax {+Err}or\", \"Syntax error (line 1, column 9): unexpected operator `+`, expected expression.\")]\n    [InlineData(\"Syntax {1 + 2 and}or\", \"Syntax error (line 1, column 18): unexpected `}`, expected expression.\")]\n    [InlineData(\"Missing {Align,-} digits\", \"Syntax error (line 1, column 17): unexpected `}`, expected number.\")]\n    [InlineData(\"Non-digit {Align,x} specifier\", \"Syntax error (line 1, column 18): unexpected identifier `x`, expected alignment and width.\")]\n    [InlineData(\"Empty {Align,} digits\", \"Syntax error (line 1, column 14): unexpected `}`, expected alignment and width.\")]\n    public void ErrorsAreReported(string input, string error)\n    {\n        Assert.False(ExpressionTemplate.TryParse(input, null, null, null, false, null, out _, out var actual));\n        Assert.Equal(error, actual);\n    }\n\n    [Fact]\n    public void DefaultAlignmentIsNull()\n    {\n        var parser = new TemplateParser();\n        Assert.True(parser.TryParse(\"{x}\", out var template, out _));\n        var avt = Assert.IsType<FormattedExpression>(template);\n        Assert.Null(avt.Alignment);\n    }\n}"
  },
  {
    "path": "test/Serilog.Expressions.Tests/TemplateTokenizerTests.cs",
    "content": "﻿using Serilog.Expressions.Parsing;\nusing Serilog.Templates.Parsing;\nusing Xunit;\n\nusing static Serilog.Expressions.Parsing.ExpressionToken;\n\nnamespace Serilog.Expressions.Tests;\n\npublic class TemplateTokenizerTests\n{\n    public static IEnumerable<object[]> ValidCases\n    {\n        get\n        {\n            return new[]\n            {\n                [\n                    \"aa\",\n                    new[] {Text}\n                ],\n                [\n                    \"{bb}\",\n                    new[] {LBrace, Identifier, RBrace}\n                ],\n                [\n                    \"aa{bb}\",\n                    new[] {Text, LBrace, Identifier, RBrace}\n                ],\n                [\n                    \"aa{{bb}}\",\n                    new[] {Text, DoubleLBrace, Text, DoubleRBrace}\n                ],\n                [\n                    \"{ {b: b} }c\",\n                    new[] {LBrace, LBrace, Identifier, Colon, Identifier, RBrace, RBrace, Text}\n                ],\n                new object[]\n                {\n                    \"{bb,-10:cc}\",\n                    new[] {LBrace, Identifier, Comma, Minus, Number, Colon, Format, RBrace}\n                },\n            };\n        }\n    }\n\n    [Theory]\n    [MemberData(nameof(ValidCases))]\n    public void ValidTemplatesAreTokenized(string template, object expected)\n    {\n        var expectedTokens = (ExpressionToken[]) expected;\n        var tokenizer = new TemplateTokenizer();\n        var actual = tokenizer.Tokenize(template).Select(t => t.Kind);\n        Assert.Equal(expectedTokens, actual);\n    }\n\n    [Theory]\n    [InlineData(\"aa{{bb}\", \"unexpected end of input, expected escaped `}`\")]\n    [InlineData(\"aa{ {b: 'b} }\", \"unexpected end of input, expected `'`\")]\n    public void InvalidTemplatesAreReported(string template, string fragment)\n    {\n        var tokenizer = new TemplateTokenizer();\n        var err = tokenizer.TryTokenize(template);\n        Assert.False(err.HasValue);\n        Assert.Equal(fragment, err.FormatErrorMessageFragment());\n    }\n}"
  },
  {
    "path": "test/Serilog.Expressions.Tests/Templates/UnreferencedPropertiesFunctionTests.cs",
    "content": "﻿using Serilog.Events;\nusing Serilog.Parsing;\nusing Serilog.Templates.Ast;\nusing Serilog.Templates.Compilation.UnreferencedProperties;\nusing Serilog.Templates.Parsing;\nusing Xunit;\n\nnamespace Serilog.Expressions.Tests.Templates;\n\npublic class UnreferencedPropertiesFunctionTests\n{\n    [Fact]\n    public void UnreferencedPropertiesFunctionIsNamedRest()\n    {\n        var function = new UnreferencedPropertiesFunction(new LiteralText(\"test\"));\n        Assert.True(function.TryResolveFunctionName(\"Rest\", out _));\n    }\n\n    [Fact]\n    public void UnreferencedPropertiesExcludeThoseInMessageAndTemplate()\n    {\n        Assert.True(new TemplateParser().TryParse(\"{@m}{A + 1}{#if true}{B}{@p.C}{@p['D']}{#end}\", out var template, out _));\n\n        var function = new UnreferencedPropertiesFunction(template);\n\n        var evt = new LogEvent(\n            DateTimeOffset.Now,\n            LogEventLevel.Debug,\n            null,\n            new(new[] {new PropertyToken(\"E\", \"{E}\")}),\n            new[]\n            {\n                new LogEventProperty(\"A\", new ScalarValue(null)),\n                new LogEventProperty(\"B\", new ScalarValue(null)),\n                new LogEventProperty(\"C\", new ScalarValue(null)),\n                new LogEventProperty(\"D\", new ScalarValue(null)),\n                new LogEventProperty(\"E\", new ScalarValue(null)),\n                new LogEventProperty(\"F\", new ScalarValue(null)),\n            });\n\n        var deep = UnreferencedPropertiesFunction.Implementation(function, evt, new ScalarValue(true));\n\n        var sv = Assert.IsType<StructureValue>(deep);\n        var included = Assert.Single(sv.Properties);\n        Assert.Equal(\"F\", included.Name);\n\n        var shallow = UnreferencedPropertiesFunction.Implementation(function, evt);\n        sv = Assert.IsType<StructureValue>(shallow);\n        Assert.Contains(sv.Properties, p => p.Name == \"E\");\n        Assert.Contains(sv.Properties, p => p.Name == \"F\");\n    }\n}"
  }
]