From f2373cf25903c2a1f3b7b5ad6431aa343ab786dd Mon Sep 17 00:00:00 2001 From: Yu Leng <42196638+moooyo@users.noreply.github.com> Date: Tue, 13 May 2025 13:46:37 +0800 Subject: [PATCH 001/117] [cmdpal] Add some logs for WinGet extension (#39329) --- .../Pages/InstallPackageListItem.cs | 17 +++++++++++++++- .../Pages/WinGetExtensionPage.cs | 20 ++++++++++++++++--- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Pages/InstallPackageListItem.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Pages/InstallPackageListItem.cs index 5c556ec3cb..8156095d85 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Pages/InstallPackageListItem.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Pages/InstallPackageListItem.cs @@ -156,7 +156,22 @@ public partial class InstallPackageListItem : ListItem private async void UpdatedInstalledStatus() { - var status = await _package.CheckInstalledStatusAsync(); + try + { + var status = await _package.CheckInstalledStatusAsync(); + } + catch (OperationCanceledException) + { + // DO NOTHING HERE + return; + } + catch (Exception ex) + { + // Handle other exceptions + ExtensionHost.LogMessage($"[WinGet] UpdatedInstalledStatus throw exception: {ex.Message}"); + return; + } + var isInstalled = _package.InstalledVersion != null; var installedState = isInstalled ? diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Pages/WinGetExtensionPage.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Pages/WinGetExtensionPage.cs index a645ff9e8e..000e2a9bba 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Pages/WinGetExtensionPage.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Pages/WinGetExtensionPage.cs @@ -57,7 +57,7 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable { // emptySearchForTag === // we don't have results yet, we haven't typed anything, and we're searching for a tag - bool emptySearchForTag = _results == null && + var emptySearchForTag = _results == null && string.IsNullOrEmpty(SearchText) && HasTag; @@ -116,8 +116,22 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable IsLoading = true; - // Save the latest search task - _currentSearchTask = DoSearchAsync(newSearch, cancellationToken); + try + { + // Save the latest search task + _currentSearchTask = DoSearchAsync(newSearch, cancellationToken); + } + catch (OperationCanceledException) + { + // DO NOTHING HERE + return; + } + catch (Exception ex) + { + // Handle other exceptions + ExtensionHost.LogMessage($"[WinGet] DoUpdateSearchText throw exception: {ex.Message}"); + return; + } // Await the task to ensure only the latest one gets processed _ = ProcessSearchResultsAsync(_currentSearchTask, newSearch); From f0a23ceaeb9fb820ded7e44d9e442ce1b36d9aaf Mon Sep 17 00:00:00 2001 From: leileizhang Date: Tue, 13 May 2025 14:39:16 +0800 Subject: [PATCH 002/117] [Fuzzing test] Use valid areaPath in OneFuzz configuration (#39393) --- .../AdvancedPaste.FuzzTests/OneFuzzConfig.json | 2 +- src/modules/Hosts/Hosts.FuzzTests/OneFuzzConfig.json | 8 ++++---- .../PowerRename.FuzzingTest/OneFuzzConfig.json | 2 +- .../RegistryPreview.FuzzTests/OneFuzzConfig.json | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/modules/AdvancedPaste/AdvancedPaste.FuzzTests/OneFuzzConfig.json b/src/modules/AdvancedPaste/AdvancedPaste.FuzzTests/OneFuzzConfig.json index bad72df342..bec199b9c0 100644 --- a/src/modules/AdvancedPaste/AdvancedPaste.FuzzTests/OneFuzzConfig.json +++ b/src/modules/AdvancedPaste/AdvancedPaste.FuzzTests/OneFuzzConfig.json @@ -17,7 +17,7 @@ "org": "microsoft", "project": "OS", "AssignedTo": "leilzh@microsoft.com", - "AreaPath": "OS\\Windows Client and Services\\WinPD\\DEEP-Developer Experience, Ecosystem and Partnerships\\DIVE\\SALT", + "AreaPath": "OS\\Windows Client and Services\\WinPD\\DFX-Developer Fundamentals and Experiences\\DEFT\\SALT", "IterationPath": "OS\\Future" }, "jobNotificationEmail": "leilzh@microsoft.com", diff --git a/src/modules/Hosts/Hosts.FuzzTests/OneFuzzConfig.json b/src/modules/Hosts/Hosts.FuzzTests/OneFuzzConfig.json index a6cb6ef39d..7f145de15d 100644 --- a/src/modules/Hosts/Hosts.FuzzTests/OneFuzzConfig.json +++ b/src/modules/Hosts/Hosts.FuzzTests/OneFuzzConfig.json @@ -17,7 +17,7 @@ "org": "microsoft", "project": "OS", "AssignedTo": "mengyuanchen@microsoft.com", - "AreaPath": "OS\\Windows Client and Services\\WinPD\\DEEP-Developer Experience, Ecosystem and Partnerships\\DIVE\\SALT", + "AreaPath": "OS\\Windows Client and Services\\WinPD\\DFX-Developer Fundamentals and Experiences\\DEFT\\SALT", "IterationPath": "OS\\Future" }, "jobNotificationEmail": "mengyuanchen@microsoft.com", @@ -58,7 +58,7 @@ "org": "microsoft", "project": "OS", "AssignedTo": "mengyuanchen@microsoft.com", - "AreaPath": "OS\\Windows Client and Services\\WinPD\\DEEP-Developer Experience, Ecosystem and Partnerships\\DIVE\\SALT", + "AreaPath": "OS\\Windows Client and Services\\WinPD\\DFX-Developer Fundamentals and Experiences\\DEFT\\SALT", "IterationPath": "OS\\Future" }, "jobNotificationEmail": "mengyuanchen@microsoft.com", @@ -99,7 +99,7 @@ "org": "microsoft", "project": "OS", "AssignedTo": "mengyuanchen@microsoft.com", - "AreaPath": "OS\\Windows Client and Services\\WinPD\\DEEP-Developer Experience, Ecosystem and Partnerships\\DIVE\\SALT", + "AreaPath": "OS\\Windows Client and Services\\WinPD\\DFX-Developer Fundamentals and Experiences\\DEFT\\SALT", "IterationPath": "OS\\Future" }, "jobNotificationEmail": "mengyuanchen@microsoft.com", @@ -140,7 +140,7 @@ "org": "microsoft", "project": "OS", "AssignedTo": "mengyuanchen@microsoft.com", - "AreaPath": "OS\\Windows Client and Services\\WinPD\\DEEP-Developer Experience, Ecosystem and Partnerships\\DIVE\\SALT", + "AreaPath": "OS\\Windows Client and Services\\WinPD\\DFX-Developer Fundamentals and Experiences\\DEFT\\SALT", "IterationPath": "OS\\Future" }, "jobNotificationEmail": "mengyuanchen@microsoft.com", diff --git a/src/modules/powerrename/PowerRename.FuzzingTest/OneFuzzConfig.json b/src/modules/powerrename/PowerRename.FuzzingTest/OneFuzzConfig.json index 41ceb9414a..a6e766002d 100644 --- a/src/modules/powerrename/PowerRename.FuzzingTest/OneFuzzConfig.json +++ b/src/modules/powerrename/PowerRename.FuzzingTest/OneFuzzConfig.json @@ -12,7 +12,7 @@ "org": "microsoft", "project": "OS", "AssignedTo": "leilzh@microsoft.com", - "AreaPath": "OS\\Windows Client and Services\\WinPD\\DEEP-Developer Experience, Ecosystem and Partnerships\\DIVE\\SALT", + "AreaPath": "OS\\Windows Client and Services\\WinPD\\DFX-Developer Fundamentals and Experiences\\DEFT\\SALT", "IterationPath": "OS\\Future" }, "jobNotificationEmail": "PowerToys@microsoft.com", diff --git a/src/modules/registrypreview/RegistryPreview.FuzzTests/OneFuzzConfig.json b/src/modules/registrypreview/RegistryPreview.FuzzTests/OneFuzzConfig.json index db58123701..13cff209b2 100644 --- a/src/modules/registrypreview/RegistryPreview.FuzzTests/OneFuzzConfig.json +++ b/src/modules/registrypreview/RegistryPreview.FuzzTests/OneFuzzConfig.json @@ -20,7 +20,7 @@ "org": "microsoft", "project": "OS", "AssignedTo": "mengyuanchen@microsoft.com", - "AreaPath": "OS\\Windows Client and Services\\WinPD\\DEEP-Developer Experience, Ecosystem and Partnerships\\DIVE\\SALT", + "AreaPath": "OS\\Windows Client and Services\\WinPD\\DFX-Developer Fundamentals and Experiences\\DEFT\\SALT", "IterationPath": "OS\\Future" }, "jobNotificationEmail": "mengyuanchen@microsoft.com", @@ -61,7 +61,7 @@ "org": "microsoft", "project": "OS", "AssignedTo": "mengyuanchen@microsoft.com", - "AreaPath": "OS\\Windows Client and Services\\WinPD\\DEEP-Developer Experience, Ecosystem and Partnerships\\DIVE\\SALT", + "AreaPath": "OS\\Windows Client and Services\\WinPD\\DFX-Developer Fundamentals and Experiences\\DEFT\\SALT", "IterationPath": "OS\\Future" }, "jobNotificationEmail": "mengyuanchen@microsoft.com", From 8a07b7b5600e00926b4585e85ed00702223b5f7b Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 13 May 2025 12:24:26 -0500 Subject: [PATCH 003/117] Bump our telemetry package version (#39388) Data collection is hard. Our internal package, which was last bumped around August 2024, mistakenly changed a load bearing string from `ETW_GROUP` to `MSPG_GROUP`. The former sets the ETW group id. The later does nothing. This PR represents bumping our dependency to the version with the fix. Considering that none of our data for CmdPal worked anyways, I took the opportunity to rename a bunch of our events that had totally generic names. Closes #38704 re: #38032 regressed around: #34078 --- .pipelines/packages.config | 2 +- .../cmdpal/Microsoft.CmdPal.UI/Events/BeginInvoke.cs | 6 +++++- src/modules/cmdpal/Microsoft.CmdPal.UI/Events/ColdLaunch.cs | 6 +++++- src/modules/cmdpal/Microsoft.CmdPal.UI/Events/OpenPage.cs | 3 ++- .../cmdpal/Microsoft.CmdPal.UI/Events/ReactivateInstance.cs | 6 +++++- 5 files changed, 18 insertions(+), 5 deletions(-) diff --git a/.pipelines/packages.config b/.pipelines/packages.config index 43fa34c91c..c4bca409f9 100644 --- a/.pipelines/packages.config +++ b/.pipelines/packages.config @@ -1,4 +1,4 @@ - + diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Events/BeginInvoke.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/Events/BeginInvoke.cs index d6e8fcd423..37659cbd31 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Events/BeginInvoke.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Events/BeginInvoke.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics.Tracing; -using Microsoft.CommandPalette.Extensions; using Microsoft.PowerToys.Telemetry; using Microsoft.PowerToys.Telemetry.Events; @@ -13,4 +12,9 @@ namespace Microsoft.CmdPal.UI.Events; public class BeginInvoke : EventBase, IEvent { public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage; + + public BeginInvoke() + { + EventName = "CmdPal_BeginInvoke"; + } } diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Events/ColdLaunch.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/Events/ColdLaunch.cs index cc85a4ec3c..0da950d5c4 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Events/ColdLaunch.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Events/ColdLaunch.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics.Tracing; -using Microsoft.CommandPalette.Extensions; using Microsoft.PowerToys.Telemetry; using Microsoft.PowerToys.Telemetry.Events; @@ -13,4 +12,9 @@ namespace Microsoft.CmdPal.UI.Events; public class ColdLaunch : EventBase, IEvent { public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage; + + public ColdLaunch() + { + EventName = "CmdPal_ColdLaunch"; + } } diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Events/OpenPage.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/Events/OpenPage.cs index 6510c06651..8ff7107104 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Events/OpenPage.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Events/OpenPage.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics.Tracing; -using Microsoft.CommandPalette.Extensions; using Microsoft.PowerToys.Telemetry; using Microsoft.PowerToys.Telemetry.Events; @@ -17,6 +16,8 @@ public class OpenPage : EventBase, IEvent public OpenPage(int pageDepth) { PageDepth = pageDepth; + + EventName = "CmdPal_OpenPage"; } public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage; diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Events/ReactivateInstance.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/Events/ReactivateInstance.cs index 7387881f26..c5f70d2905 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Events/ReactivateInstance.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Events/ReactivateInstance.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics.Tracing; -using Microsoft.CommandPalette.Extensions; using Microsoft.PowerToys.Telemetry; using Microsoft.PowerToys.Telemetry.Events; @@ -13,4 +12,9 @@ namespace Microsoft.CmdPal.UI.Events; public class ReactivateInstance : EventBase, IEvent { public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage; + + public ReactivateInstance() + { + EventName = "CmdPal_ReactivateInstance"; + } } From 797941954d3d196a0146e658c965640d2f0450b4 Mon Sep 17 00:00:00 2001 From: Jeremy Sinclair <4016293+snickler@users.noreply.github.com> Date: Tue, 13 May 2025 19:59:01 -0400 Subject: [PATCH 004/117] [Deps] Update .NET packages from 9.0.4 to 9.0.5 (#39404) Updates .NET 9 Runtime / Library packages to the latest 9.0.5 servicing release for security fixes. This PR also updates the version of System.Text.Json to 9.0.5 in the CmdPal extension template. --- Directory.Packages.props | 42 +++++++++---------- NOTICE.md | 42 +++++++++---------- .../Directory.Packages.props | 2 +- 3 files changed, 43 insertions(+), 43 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index b3b404ea51..083a66e5a9 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -31,22 +31,22 @@ - + - + - - - - - + + + + + - + - + - + - - - + + + - + - - + + - + - - - - + + + + diff --git a/NOTICE.md b/NOTICE.md index d604be7d7f..21d51192c4 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -1453,22 +1453,22 @@ SOFTWARE. - Mages 3.0.0 - Markdig.Signed 0.34.0 - MessagePack 3.1.3 -- Microsoft.Bcl.AsyncInterfaces 9.0.4 +- Microsoft.Bcl.AsyncInterfaces 9.0.5 - Microsoft.CodeAnalysis.NetAnalyzers 9.0.0 -- Microsoft.Data.Sqlite 9.0.4 +- Microsoft.Data.Sqlite 9.0.5 - Microsoft.Diagnostics.Tracing.TraceEvent 3.1.16 - Microsoft.DotNet.ILCompiler (A) -- Microsoft.Extensions.DependencyInjection 9.0.4 -- Microsoft.Extensions.Hosting 9.0.4 -- Microsoft.Extensions.Hosting.WindowsServices 9.0.4 -- Microsoft.Extensions.Logging 9.0.4 -- Microsoft.Extensions.Logging.Abstractions 9.0.4 +- Microsoft.Extensions.DependencyInjection 9.0.5 +- Microsoft.Extensions.Hosting 9.0.5 +- Microsoft.Extensions.Hosting.WindowsServices 9.0.5 +- Microsoft.Extensions.Logging 9.0.5 +- Microsoft.Extensions.Logging.Abstractions 9.0.5 - Microsoft.NET.ILLink.Tasks (A) - Microsoft.SemanticKernel 1.15.0 - Microsoft.Toolkit.Uwp.Notifications 7.1.2 - Microsoft.Web.WebView2 1.0.2903.40 -- Microsoft.Win32.SystemEvents 9.0.4 -- Microsoft.Windows.Compatibility 9.0.4 +- Microsoft.Win32.SystemEvents 9.0.5 +- Microsoft.Windows.Compatibility 9.0.5 - Microsoft.Windows.CsWin32 0.2.46-beta - Microsoft.Windows.CsWinRT 2.2.0 - Microsoft.Windows.SDK.BuildTools 10.0.22621.2428 @@ -1487,25 +1487,25 @@ SOFTWARE. - SharpCompress 0.37.2 - StreamJsonRpc 2.21.69 - StyleCop.Analyzers 1.2.0-beta.556 -- System.CodeDom 9.0.4 +- System.CodeDom 9.0.5 - System.CommandLine 2.0.0-beta4.22272.1 -- System.ComponentModel.Composition 9.0.4 -- System.Configuration.ConfigurationManager 9.0.4 -- System.Data.OleDb 9.0.4 +- System.ComponentModel.Composition 9.0.5 +- System.Configuration.ConfigurationManager 9.0.5 +- System.Data.OleDb 9.0.5 - System.Data.SqlClient 4.8.6 -- System.Diagnostics.EventLog 9.0.4 -- System.Diagnostics.PerformanceCounter 9.0.4 -- System.Drawing.Common 9.0.4 +- System.Diagnostics.EventLog 9.0.5 +- System.Diagnostics.PerformanceCounter 9.0.5 +- System.Drawing.Common 9.0.5 - System.IO.Abstractions 22.0.13 - System.IO.Abstractions.TestingHelpers 22.0.13 -- System.Management 9.0.4 +- System.Management 9.0.5 - System.Net.Http 4.3.4 - System.Private.Uri 4.3.2 - System.Reactive 6.0.1 -- System.Runtime.Caching 9.0.4 -- System.ServiceProcess.ServiceController 9.0.4 -- System.Text.Encoding.CodePages 9.0.4 -- System.Text.Json 9.0.4 +- System.Runtime.Caching 9.0.5 +- System.ServiceProcess.ServiceController 9.0.5 +- System.Text.Encoding.CodePages 9.0.5 +- System.Text.Json 9.0.5 - System.Text.RegularExpressions 4.3.1 - UnicodeInformation 2.6.0 - UnitsNet 5.56.0 diff --git a/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/Directory.Packages.props b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/Directory.Packages.props index 049dc46978..7a8dfdc97a 100644 --- a/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/Directory.Packages.props +++ b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/Directory.Packages.props @@ -12,6 +12,6 @@ - + From 469ffd93ea983f80479715ecdc51cf7be98219c3 Mon Sep 17 00:00:00 2001 From: Mengyuan <162882040+chenmy77@users.noreply.github.com> Date: Wed, 14 May 2025 09:30:44 +0800 Subject: [PATCH 005/117] [Fuzz] Add DLL Reference in OneFuzzConfig.json to Fix Hosts Fuzz Bug (#39398) * add Testably.Abstractions.FileSystem.Interface.dll * fix spell error --- .github/actions/spell-check/allow/code.txt | 1 + src/modules/Hosts/Hosts.FuzzTests/OneFuzzConfig.json | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/actions/spell-check/allow/code.txt b/.github/actions/spell-check/allow/code.txt index 10e9473258..ef01f0d7c3 100644 --- a/.github/actions/spell-check/allow/code.txt +++ b/.github/actions/spell-check/allow/code.txt @@ -271,6 +271,7 @@ mengyuanchen # DllName testhost +Testably #Tools OIP \ No newline at end of file diff --git a/src/modules/Hosts/Hosts.FuzzTests/OneFuzzConfig.json b/src/modules/Hosts/Hosts.FuzzTests/OneFuzzConfig.json index 7f145de15d..7530d3292c 100644 --- a/src/modules/Hosts/Hosts.FuzzTests/OneFuzzConfig.json +++ b/src/modules/Hosts/Hosts.FuzzTests/OneFuzzConfig.json @@ -158,19 +158,19 @@ // the DLL and PDB files // you will need to add any other files required // (globs are supported) + "Castle.Core.dll", + "CommunityToolkit.Mvvm.dll", "Hosts.FuzzTests.dll", "Hosts.FuzzTests.pdb", "Microsoft.Windows.SDK.NET.dll", - "WinRT.Runtime.dll", "Moq.dll", - "testhost.dll", - "Castle.Core.dll", "System.IO.Abstractions.dll", - "CommunityToolkit.Mvvm.dll", "System.IO.Abstractions.TestingHelpers.dll", "TestableIO.System.IO.Abstractions.dll", "TestableIO.System.IO.Abstractions.TestingHelpers.dll", - "TestableIO.System.IO.Abstractions.Wrappers.dll" + "TestableIO.System.IO.Abstractions.Wrappers.dll", + "Testably.Abstractions.FileSystem.Interface.dll", + "WinRT.Runtime.dll" ], "SdlWorkItemId": 49911822 } From a71cc282d3e7e9c4465fe87374c84b83dc2e5825 Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Wed, 14 May 2025 12:15:19 -0700 Subject: [PATCH 006/117] cmdpal: use the unified Windows Terminal Versioning scheme (#39320) This pull request adopts the unified versioning scheme used by Windows Terminal, Notepad, and hundreds of other internal and public projects that relied on "XES" or "PackageES". It only does so for the command palette. All command palette assets will be versioned according to the Major and Minor number in `src/modules/cmdpal/custom.props`. This includes DLLs, EXEs, NuGet packages and MSIX bundles. This will ensure that all artifacts that we produce are versioned properly: | thing | version (ex.) | |---------|-----------------| | dll/exe | 0.2.2505.08001 | | nupkg | 0.2.250508001 | | appx | 0.2.3269.0 | For reference, here's the version format: ### EXE, DLL, .NET Assembly 0.2.2505.08001 ^ ^ ^ ^ ^ ^ | | | | | `-Build # on that date | | | | `-Day | | | `-Month | | `-Year | `-Minor `-Major ### NuGet Package 0.2.250508001 ^ ^ ^ ^ ^ ^ | | | | | `-Build # on that date | | | | `-Day | | | `-Month | | `-Year | `-Minor `-Major ### AppX Package 0.2.01281.0 (the leading 0 will be removed) ^ ^ ^ ^^ ^ | | | || `-Contractually always zero (a waste) | | | |`-Build # on that date | | | `-Number of days in [base year] | | `-Number of years since [base year] | `-Minor `-Major [base year] = $(XesBaseYearForStoreVersion) It is expected that the base year is changed every time the version number is changed. --- .github/actions/spell-check/allow/code.txt | 6 ++- .pipelines/v2/release.yml | 19 +++------ .pipelines/v2/templates/job-build-project.yml | 11 +++-- .pipelines/v2/templates/job-build-sdk.yml | 19 ++++++--- .../v2/templates/steps-setup-versioning.yml | 11 +++++ .../variables-nuget-package-version.yml | 17 ++++++++ .pipelines/versionSetting.ps1 | 15 +------ installer/PowerToysSetup/CmdPal.wxs | 14 +++---- src/CmdPalVersion.props | 3 +- src/Common.Dotnet.AotCompatibility.props | 2 +- src/Common.Dotnet.CsWinRT.props | 2 +- .../CmdPalKeyboardService.rc | 40 ------------------- .../CmdPalKeyboardService.vcxproj | 4 -- .../CmdPalKeyboardService.vcxproj.filters | 8 +--- .../cmdpal/CmdPalKeyboardService/resource.h | 13 ------ .../CmdPalModuleInterface.rc | 40 ------------------- .../CmdPalModuleInterface.vcxproj | 9 +---- .../cmdpal/CmdPalModuleInterface/resource.h | 13 ------ .../Microsoft.CmdPal.UI/CmdPal.pre.props | 2 + .../Microsoft.CmdPal.UI.csproj | 12 ++++++ .../Microsoft.Terminal.UI.vcxproj | 11 +---- .../cmdpal/Microsoft.Terminal.UI/resource.h | 13 ------ .../cmdpal/Microsoft.Terminal.UI/version.rc | 40 ------------------- src/modules/cmdpal/custom.props | 11 +++++ ...t.CommandPalette.Extensions.Toolkit.csproj | 2 +- ...icrosoft.CommandPalette.Extensions.vcxproj | 8 ---- .../version.rc | 34 ---------------- .../extensionsdk/nuget/BuildSDKHelper.ps1 | 10 ++++- 28 files changed, 111 insertions(+), 278 deletions(-) create mode 100644 .pipelines/v2/templates/steps-setup-versioning.yml create mode 100644 .pipelines/v2/templates/variables-nuget-package-version.yml delete mode 100644 src/modules/cmdpal/CmdPalKeyboardService/CmdPalKeyboardService.rc delete mode 100644 src/modules/cmdpal/CmdPalKeyboardService/resource.h delete mode 100644 src/modules/cmdpal/CmdPalModuleInterface/CmdPalModuleInterface.rc delete mode 100644 src/modules/cmdpal/CmdPalModuleInterface/resource.h delete mode 100644 src/modules/cmdpal/Microsoft.Terminal.UI/resource.h delete mode 100644 src/modules/cmdpal/Microsoft.Terminal.UI/version.rc create mode 100644 src/modules/cmdpal/custom.props delete mode 100644 src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/version.rc diff --git a/.github/actions/spell-check/allow/code.txt b/.github/actions/spell-check/allow/code.txt index ef01f0d7c3..cf4af7fa8b 100644 --- a/.github/actions/spell-check/allow/code.txt +++ b/.github/actions/spell-check/allow/code.txt @@ -274,4 +274,8 @@ testhost Testably #Tools -OIP \ No newline at end of file +OIP +xef +xes +PACKAGEVERSIONNUMBER +APPXMANIFESTVERSION diff --git a/.pipelines/v2/release.yml b/.pipelines/v2/release.yml index bd825355dd..227dd1cf71 100644 --- a/.pipelines/v2/release.yml +++ b/.pipelines/v2/release.yml @@ -20,16 +20,6 @@ parameters: type: string default: '0.0.1' - - name: cmdPalVersionNumber - displayName: "Command Palette Version Number" - type: string - default: '0.0.1' - - - name: cmdPalSdkVersionNumber - displayName: "Command Palette SDK Version Number" - type: string - default: '0.0.1' - - name: buildConfigurations displayName: "Build Configurations" type: object @@ -50,6 +40,9 @@ parameters: name: $(BuildDefinitionName)_$(date:yyMM).$(date:dd)$(rev:rrr) +variables: + - template: templates/variables-nuget-package-version.yml + extends: template: v1/1ES.Official.PipelineTemplate.yml@1ESPipelineTemplates parameters: @@ -88,8 +81,8 @@ extends: buildPlatforms: ${{ parameters.buildPlatforms }} buildConfigurations: ${{ parameters.buildConfigurations }} versionNumber: ${{ parameters.versionNumber }} - cmdPalVersionNumber: ${{ parameters.cmdPalVersionNumber }} publishArtifacts: false # 1ES PT handles publication for us. + official: true codeSign: true runTests: false signingIdentity: @@ -106,7 +99,7 @@ extends: beforeBuildSteps: # Sets versions for all PowerToy created DLLs - pwsh: |- - .pipelines/versionSetting.ps1 -versionNumber '${{ parameters.versionNumber }}' -DevEnvironment '' -cmdPalVersionNumber '${{ parameters.cmdPalVersionNumber }}' + .pipelines/versionSetting.ps1 -versionNumber '${{ parameters.versionNumber }}' -DevEnvironment '' displayName: Prepare versioning # Prepare the localizations and telemetry config before the release build @@ -130,8 +123,8 @@ extends: name: SHINE-INT-L image: SHINE-VS17-Latest os: windows + official: true codeSign: true - sdkVersionNumber: ${{ parameters.cmdPalSdkVersionNumber }} signingIdentity: serviceName: $(SigningServiceName) appId: $(SigningAppId) diff --git a/.pipelines/v2/templates/job-build-project.yml b/.pipelines/v2/templates/job-build-project.yml index 34ac24ae41..d9d8da5773 100644 --- a/.pipelines/v2/templates/job-build-project.yml +++ b/.pipelines/v2/templates/job-build-project.yml @@ -11,6 +11,9 @@ parameters: default: - x64 - arm64 + - name: official + type: boolean + default: false - name: codeSign type: boolean default: false @@ -56,9 +59,6 @@ parameters: - name: versionNumber type: string default: '0.0.1' - - name: cmdPalVersionNumber - type: string - default: '0.0.1' - name: useLatestWinAppSDK type: boolean default: false @@ -215,6 +215,11 @@ jobs: env: VCWhereExtraVersionTarget: '-prerelease' + - ${{ if eq(parameters.official, true) }}: + - template: .\steps-setup-versioning.yml + parameters: + directory: $(build.sourcesdirectory)\src\modules\cmdpal + - pwsh: |- & "$(build.sourcesdirectory)\.pipelines\installWiX.ps1" displayName: Download and install WiX 3.14 development build diff --git a/.pipelines/v2/templates/job-build-sdk.yml b/.pipelines/v2/templates/job-build-sdk.yml index f8aa5dca3e..f8cb9c930a 100644 --- a/.pipelines/v2/templates/job-build-sdk.yml +++ b/.pipelines/v2/templates/job-build-sdk.yml @@ -3,6 +3,9 @@ parameters: type: object default: - Release + - name: official + type: boolean + default: false - name: codeSign type: boolean default: false @@ -12,9 +15,6 @@ parameters: - name: signingIdentity type: object default: {} - - name: sdkVersionNumber - type: string - default: '0.0.1' jobs: - job: "BuildSDK" @@ -36,8 +36,17 @@ jobs: fetchTags: false fetchDepth: 1 + - template: .\steps-ensure-nuget-version.yml + + - task: NuGetAuthenticate@1 + + - ${{ if eq(parameters.official, true) }}: + - template: .\steps-setup-versioning.yml + parameters: + directory: $(build.sourcesdirectory)\src\modules\cmdpal + - pwsh: |- - & "$(build.sourcesdirectory)\src\modules\cmdpal\extensionsdk\nuget\BuildSDKHelper.ps1" -Configuration "Release" -VersionOfSDK ${{ parameters.sdkVersionNumber }} -BuildStep "build" -IsAzurePipelineBuild + & "$(build.sourcesdirectory)\src\modules\cmdpal\extensionsdk\nuget\BuildSDKHelper.ps1" -Configuration "Release" -BuildStep "build" -IsAzurePipelineBuild displayName: Build SDK - ${{ if eq(parameters.codeSign, true) }}: @@ -52,7 +61,7 @@ jobs: ciPolicyFile: '$(build.sourcesdirectory)\.pipelines\CIPolicy.xml' - pwsh: |- - & "$(build.sourcesdirectory)\src\modules\cmdpal\extensionsdk\nuget\BuildSDKHelper.ps1" -Configuration "Release" -VersionOfSDK ${{ parameters.sdkVersionNumber }} -BuildStep "pack" -IsAzurePipelineBuild + & "$(build.sourcesdirectory)\src\modules\cmdpal\extensionsdk\nuget\BuildSDKHelper.ps1" -Configuration "Release" -BuildStep "pack" -IsAzurePipelineBuild displayName: Pack SDK - task: CopyFiles@2 diff --git a/.pipelines/v2/templates/steps-setup-versioning.yml b/.pipelines/v2/templates/steps-setup-versioning.yml new file mode 100644 index 0000000000..6dc0e3ef92 --- /dev/null +++ b/.pipelines/v2/templates/steps-setup-versioning.yml @@ -0,0 +1,11 @@ +parameters: + - name: directory + type: string + default: $(Build.SourcesDirectory) + +steps: + - pwsh: |- + nuget install Microsoft.Windows.Terminal.Versioning -ConfigFile "$(Build.SourcesDirectory)\.pipelines\release-nuget.config" -OutputDirectory _versioning + $VersionRoot = (Get-Item _versioning\Microsoft.Windows.*).FullName + & "$VersionRoot\build\Setup.ps1" -ProjectDirectory "${{ parameters.directory }}" -Verbose + displayName: Set up versioning for ${{ parameters.directory }} via M.W.T.V diff --git a/.pipelines/v2/templates/variables-nuget-package-version.yml b/.pipelines/v2/templates/variables-nuget-package-version.yml new file mode 100644 index 0000000000..460b7ceee0 --- /dev/null +++ b/.pipelines/v2/templates/variables-nuget-package-version.yml @@ -0,0 +1,17 @@ +variables: + # If we are building a branch called "stable*", hide the NuGet suffix. + # If we don't do that, XES will set the suffix to "stable". + # main is special, however. XES ignores main. Since we never produce actual + # shipping builds from main, we want to force it to have a beta label. + # + # In effect: + # BRANCH / BRANDING | Version | + # ------------------|----------------------------| + # stable | 0.2.250512001 | + # main | 0.2.250512001-experimental | + # all others | 0.2.250512001-branch | + ${{ if startsWith(variables['Build.SourceBranchName'], 'stable') }}: + NoNuGetPackBetaVersion: true + ${{ elseif eq(variables['Build.SourceBranchName'], 'main') }}: + NuGetPackBetaVersion: experimental + diff --git a/.pipelines/versionSetting.ps1 b/.pipelines/versionSetting.ps1 index bda3c47cc2..cf2d2595af 100644 --- a/.pipelines/versionSetting.ps1 +++ b/.pipelines/versionSetting.ps1 @@ -5,10 +5,7 @@ Param( [Parameter(Mandatory=$True,Position=2)] [AllowEmptyString()] - [string]$DevEnvironment = "Local", - - [Parameter(Mandatory=$True,Position=3)] - [string]$cmdPalVersionNumber = "0.0.1" + [string]$DevEnvironment = "Local" ) Write-Host $PSScriptRoot @@ -49,7 +46,6 @@ $verProps.Save($verPropWriteFileLocation); $verPropWriteFileLocation = $PSScriptRoot + '/../src/CmdPalVersion.props'; $verPropReadFileLocation = $verPropWriteFileLocation; [XML]$verProps = Get-Content $verPropReadFileLocation -$verProps.Project.PropertyGroup.CmdPalVersion = $cmdPalVersionNumber; $verProps.Project.PropertyGroup.DevEnvironment = $DevEnvironment; Write-Host "xml" $verProps.Project.PropertyGroup.Version $verProps.Save($verPropWriteFileLocation); @@ -90,12 +86,3 @@ $newPlusContextMenuAppManifestReadFileLocation = $newPlusContextMenuAppManifestW $newPlusContextMenuAppManifest.Package.Identity.Version = $versionNumber + '.0' Write-Host "NewPlusContextMenu version" $newPlusContextMenuAppManifest.Package.Identity.Version $newPlusContextMenuAppManifest.Save($newPlusContextMenuAppManifestWriteFileLocation); - -# Set package version in Package.appxmanifest -$cmdPalAppManifestWriteFileLocation = $PSScriptRoot + '/../src/modules/cmdpal/Microsoft.CmdPal.UI/Package.appxmanifest'; -$cmdPalAppManifestReadFileLocation = $cmdPalAppManifestWriteFileLocation; - -[XML]$cmdPalAppManifest = Get-Content $cmdPalAppManifestReadFileLocation -$cmdPalAppManifest.Package.Identity.Version = $cmdPalVersionNumber + '.0' -Write-Host "CmdPal Package version: " $cmdPalAppManifest.Package.Identity.Version -$cmdPalAppManifest.Save($cmdPalAppManifestWriteFileLocation); diff --git a/installer/PowerToysSetup/CmdPal.wxs b/installer/PowerToysSetup/CmdPal.wxs index c74e27d6c1..c3c5280cc5 100644 --- a/installer/PowerToysSetup/CmdPal.wxs +++ b/installer/PowerToysSetup/CmdPal.wxs @@ -19,36 +19,36 @@ - + - + - + - + - + - + - + diff --git a/src/CmdPalVersion.props b/src/CmdPalVersion.props index e9e0e98130..2be9bc69d4 100644 --- a/src/CmdPalVersion.props +++ b/src/CmdPalVersion.props @@ -1,7 +1,8 @@ - 0.0.1 + $(XES_APPXMANIFESTVERSION) + 0.0.1.0 Local diff --git a/src/Common.Dotnet.AotCompatibility.props b/src/Common.Dotnet.AotCompatibility.props index 71c490fd6c..46a39e9c65 100644 --- a/src/Common.Dotnet.AotCompatibility.props +++ b/src/Common.Dotnet.AotCompatibility.props @@ -7,6 +7,6 @@ 2 - IL2081 + IL2081;$(WarningsNotAsErrors) diff --git a/src/Common.Dotnet.CsWinRT.props b/src/Common.Dotnet.CsWinRT.props index cde2ce69ce..d37c39685a 100644 --- a/src/Common.Dotnet.CsWinRT.props +++ b/src/Common.Dotnet.CsWinRT.props @@ -16,7 +16,7 @@ 4 True - CA1720;CA1859;CA2263;CA2022;MVVMTK0045;MVVMTK0049 + CA1824;CA1416;CA1720;CA1859;CA2263;CA2022;MVVMTK0045;MVVMTK0049 diff --git a/src/modules/cmdpal/CmdPalKeyboardService/CmdPalKeyboardService.rc b/src/modules/cmdpal/CmdPalKeyboardService/CmdPalKeyboardService.rc deleted file mode 100644 index 5fa3c8b90d..0000000000 --- a/src/modules/cmdpal/CmdPalKeyboardService/CmdPalKeyboardService.rc +++ /dev/null @@ -1,40 +0,0 @@ -#include -#include "resource.h" -#include "../../../common/version/version.h" - -#define APSTUDIO_READONLY_SYMBOLS -#include "winres.h" -#undef APSTUDIO_READONLY_SYMBOLS - -1 VERSIONINFO -FILEVERSION FILE_VERSION -PRODUCTVERSION PRODUCT_VERSION -FILEFLAGSMASK VS_FFI_FILEFLAGSMASK -#ifdef _DEBUG -FILEFLAGS VS_FF_DEBUG -#else -FILEFLAGS 0x0L -#endif -FILEOS VOS_NT_WINDOWS32 -FILETYPE VFT_DLL -FILESUBTYPE VFT2_UNKNOWN -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset - BEGIN - VALUE "CompanyName", COMPANY_NAME - VALUE "FileDescription", FILE_DESCRIPTION - VALUE "FileVersion", FILE_VERSION_STRING - VALUE "InternalName", INTERNAL_NAME - VALUE "LegalCopyright", COPYRIGHT_NOTE - VALUE "OriginalFilename", ORIGINAL_FILENAME - VALUE "ProductName", PRODUCT_NAME - VALUE "ProductVersion", PRODUCT_VERSION_STRING - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1200 // US English (0x0409), Unicode (1200) charset - END -END diff --git a/src/modules/cmdpal/CmdPalKeyboardService/CmdPalKeyboardService.vcxproj b/src/modules/cmdpal/CmdPalKeyboardService/CmdPalKeyboardService.vcxproj index e110d736ae..f891ce96e6 100644 --- a/src/modules/cmdpal/CmdPalKeyboardService/CmdPalKeyboardService.vcxproj +++ b/src/modules/cmdpal/CmdPalKeyboardService/CmdPalKeyboardService.vcxproj @@ -108,7 +108,6 @@ KeyboardListener.idl - @@ -130,9 +129,6 @@ - - - diff --git a/src/modules/cmdpal/CmdPalKeyboardService/CmdPalKeyboardService.vcxproj.filters b/src/modules/cmdpal/CmdPalKeyboardService/CmdPalKeyboardService.vcxproj.filters index 301ac57f3a..2309326e71 100644 --- a/src/modules/cmdpal/CmdPalKeyboardService/CmdPalKeyboardService.vcxproj.filters +++ b/src/modules/cmdpal/CmdPalKeyboardService/CmdPalKeyboardService.vcxproj.filters @@ -16,7 +16,6 @@ - @@ -28,9 +27,4 @@ - - - Resources - - - \ No newline at end of file + diff --git a/src/modules/cmdpal/CmdPalKeyboardService/resource.h b/src/modules/cmdpal/CmdPalKeyboardService/resource.h deleted file mode 100644 index f99c3adb95..0000000000 --- a/src/modules/cmdpal/CmdPalKeyboardService/resource.h +++ /dev/null @@ -1,13 +0,0 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by PowerToys.MeasureToolCore.rc - -////////////////////////////// -// Non-localizable - -#define FILE_DESCRIPTION "CmdPalKeyboardService" -#define INTERNAL_NAME "CmdPalKeyboardService" -#define ORIGINAL_FILENAME "CmdPalKeyboardService.dll" - -// Non-localizable -////////////////////////////// diff --git a/src/modules/cmdpal/CmdPalModuleInterface/CmdPalModuleInterface.rc b/src/modules/cmdpal/CmdPalModuleInterface/CmdPalModuleInterface.rc deleted file mode 100644 index 5fa3c8b90d..0000000000 --- a/src/modules/cmdpal/CmdPalModuleInterface/CmdPalModuleInterface.rc +++ /dev/null @@ -1,40 +0,0 @@ -#include -#include "resource.h" -#include "../../../common/version/version.h" - -#define APSTUDIO_READONLY_SYMBOLS -#include "winres.h" -#undef APSTUDIO_READONLY_SYMBOLS - -1 VERSIONINFO -FILEVERSION FILE_VERSION -PRODUCTVERSION PRODUCT_VERSION -FILEFLAGSMASK VS_FFI_FILEFLAGSMASK -#ifdef _DEBUG -FILEFLAGS VS_FF_DEBUG -#else -FILEFLAGS 0x0L -#endif -FILEOS VOS_NT_WINDOWS32 -FILETYPE VFT_DLL -FILESUBTYPE VFT2_UNKNOWN -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset - BEGIN - VALUE "CompanyName", COMPANY_NAME - VALUE "FileDescription", FILE_DESCRIPTION - VALUE "FileVersion", FILE_VERSION_STRING - VALUE "InternalName", INTERNAL_NAME - VALUE "LegalCopyright", COPYRIGHT_NOTE - VALUE "OriginalFilename", ORIGINAL_FILENAME - VALUE "ProductName", PRODUCT_NAME - VALUE "ProductVersion", PRODUCT_VERSION_STRING - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1200 // US English (0x0409), Unicode (1200) charset - END -END diff --git a/src/modules/cmdpal/CmdPalModuleInterface/CmdPalModuleInterface.vcxproj b/src/modules/cmdpal/CmdPalModuleInterface/CmdPalModuleInterface.vcxproj index 2d70013009..433e7599d5 100644 --- a/src/modules/cmdpal/CmdPalModuleInterface/CmdPalModuleInterface.vcxproj +++ b/src/modules/cmdpal/CmdPalModuleInterface/CmdPalModuleInterface.vcxproj @@ -44,9 +44,6 @@ {6955446d-23f7-4023-9bb3-8657f904af99} - - {cc6e41ac-8174-4e8a-8d22-85dd7f4851df} - @@ -66,7 +63,6 @@ - @@ -74,9 +70,6 @@ Create - - - @@ -92,4 +85,4 @@ - \ No newline at end of file + diff --git a/src/modules/cmdpal/CmdPalModuleInterface/resource.h b/src/modules/cmdpal/CmdPalModuleInterface/resource.h deleted file mode 100644 index 483f62d2bc..0000000000 --- a/src/modules/cmdpal/CmdPalModuleInterface/resource.h +++ /dev/null @@ -1,13 +0,0 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by AlwaysOnTopModuleInterface.rc - -////////////////////////////// -// Non-localizable - -#define FILE_DESCRIPTION "PowerToys Command Palette Module" -#define INTERNAL_NAME "PowerToys.CmdPalModuleInterface" -#define ORIGINAL_FILENAME "PowerToys.CmdPalModuleInterface.dll" - -// Non-localizable -////////////////////////////// diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/CmdPal.pre.props b/src/modules/cmdpal/Microsoft.CmdPal.UI/CmdPal.pre.props index c732a29374..21c2e7d8d1 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/CmdPal.pre.props +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/CmdPal.pre.props @@ -8,6 +8,8 @@ $(SolutionDir)$(Platform)\$(Configuration)\WinUI3Apps\CmdPal + + $(OutputPath)\AppPackages\ diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Microsoft.CmdPal.UI.csproj b/src/modules/cmdpal/Microsoft.CmdPal.UI/Microsoft.CmdPal.UI.csproj index 911997da51..b3ec6188cf 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Microsoft.CmdPal.UI.csproj +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Microsoft.CmdPal.UI.csproj @@ -38,6 +38,18 @@ DISABLE_XAML_GENERATED_MAIN + + + + + $(ApplicationManifest.Replace("$(MSBuildProjectDirectory)\","")) + + + + diff --git a/src/modules/cmdpal/Microsoft.Terminal.UI/Microsoft.Terminal.UI.vcxproj b/src/modules/cmdpal/Microsoft.Terminal.UI/Microsoft.Terminal.UI.vcxproj index 69e3c64635..a3c282b7f1 100644 --- a/src/modules/cmdpal/Microsoft.Terminal.UI/Microsoft.Terminal.UI.vcxproj +++ b/src/modules/cmdpal/Microsoft.Terminal.UI/Microsoft.Terminal.UI.vcxproj @@ -23,9 +23,6 @@ 10.0.19041.0 - - - Debug @@ -156,7 +153,6 @@ IconPathConverter.idl - ResourceString.idl @@ -205,9 +201,4 @@ - - - {cc6e41ac-8174-4e8a-8d22-85dd7f4851df} - - - \ No newline at end of file + diff --git a/src/modules/cmdpal/Microsoft.Terminal.UI/resource.h b/src/modules/cmdpal/Microsoft.Terminal.UI/resource.h deleted file mode 100644 index 85ae1a0e9b..0000000000 --- a/src/modules/cmdpal/Microsoft.Terminal.UI/resource.h +++ /dev/null @@ -1,13 +0,0 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by AlwaysOnTopModuleInterface.rc - -////////////////////////////// -// Non-localizable - -#define FILE_DESCRIPTION "PowerToys Command Palette Terminal UI" -#define INTERNAL_NAME "Microsoft.Terminal.UI" -#define ORIGINAL_FILENAME "Microsoft.Terminal.UI.dll" - -// Non-localizable -////////////////////////////// diff --git a/src/modules/cmdpal/Microsoft.Terminal.UI/version.rc b/src/modules/cmdpal/Microsoft.Terminal.UI/version.rc deleted file mode 100644 index 0bcdeca2ef..0000000000 --- a/src/modules/cmdpal/Microsoft.Terminal.UI/version.rc +++ /dev/null @@ -1,40 +0,0 @@ -#include -#include "resource.h" -#include "../../../../common/version/version.h" - -#define APSTUDIO_READONLY_SYMBOLS -#include "winres.h" -#undef APSTUDIO_READONLY_SYMBOLS - -1 VERSIONINFO -FILEVERSION FILE_VERSION -PRODUCTVERSION PRODUCT_VERSION -FILEFLAGSMASK VS_FFI_FILEFLAGSMASK -#ifdef _DEBUG -FILEFLAGS VS_FF_DEBUG -#else -FILEFLAGS 0x0L -#endif -FILEOS VOS_NT_WINDOWS32 -FILETYPE VFT_DLL -FILESUBTYPE VFT2_UNKNOWN -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset - BEGIN - VALUE "CompanyName", COMPANY_NAME - VALUE "FileDescription", FILE_DESCRIPTION - VALUE "FileVersion", FILE_VERSION_STRING - VALUE "InternalName", INTERNAL_NAME - VALUE "LegalCopyright", COPYRIGHT_NOTE - VALUE "OriginalFilename", ORIGINAL_FILENAME - VALUE "ProductName", PRODUCT_NAME - VALUE "ProductVersion", PRODUCT_VERSION_STRING - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1200 // US English (0x0409), Unicode (1200) charset - END -END diff --git a/src/modules/cmdpal/custom.props b/src/modules/cmdpal/custom.props new file mode 100644 index 0000000000..ddea8b85d2 --- /dev/null +++ b/src/modules/cmdpal/custom.props @@ -0,0 +1,11 @@ + + + + + true + 2025 + 0 + 2 + Microsoft Command Palette + + diff --git a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/Microsoft.CommandPalette.Extensions.Toolkit.csproj b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/Microsoft.CommandPalette.Extensions.Toolkit.csproj index 95dc53b444..8f3cdaed91 100644 --- a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/Microsoft.CommandPalette.Extensions.Toolkit.csproj +++ b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/Microsoft.CommandPalette.Extensions.Toolkit.csproj @@ -51,6 +51,6 @@ True - IL2081 + IL2081;$(WarningsNotAsErrors) diff --git a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/Microsoft.CommandPalette.Extensions.vcxproj b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/Microsoft.CommandPalette.Extensions.vcxproj index 0f87df5625..e7d8875326 100644 --- a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/Microsoft.CommandPalette.Extensions.vcxproj +++ b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/Microsoft.CommandPalette.Extensions.vcxproj @@ -180,12 +180,4 @@ - - - - - - {cc6e41ac-8174-4e8a-8d22-85dd7f4851df} - - diff --git a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/version.rc b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/version.rc deleted file mode 100644 index 67e50b2cbb..0000000000 --- a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/version.rc +++ /dev/null @@ -1,34 +0,0 @@ -#include "winres.h" - -#include "../../../../common/version/version.h" - -VS_VERSION_INFO VERSIONINFO - FILEVERSION FILE_VERSION - PRODUCTVERSION PRODUCT_VERSION - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS 0x40004L - FILETYPE 0x0L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904b0" - BEGIN - VALUE "CompanyName", "Microsoft Corporation" - VALUE "FileDescription", "Microsoft.CommandPalette.Extensions.Toolkit" - VALUE "FileVersion", FILE_VERSION_STRING - VALUE "ProductName", "Microsoft.CommandPalette.Extensions.Toolkit" - VALUE "ProductVersion", PRODUCT_VERSION_STRING - VALUE "LegalCopyright", "Copyright (c) Microsoft Corporation" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1200 - END -END diff --git a/src/modules/cmdpal/extensionsdk/nuget/BuildSDKHelper.ps1 b/src/modules/cmdpal/extensionsdk/nuget/BuildSDKHelper.ps1 index 179c4bfa3f..52e2f17586 100644 --- a/src/modules/cmdpal/extensionsdk/nuget/BuildSDKHelper.ps1 +++ b/src/modules/cmdpal/extensionsdk/nuget/BuildSDKHelper.ps1 @@ -1,11 +1,19 @@ Param( [string]$Configuration = "release", - [string]$VersionOfSDK = "0.0.0", + [string]$VersionOfSDK = "", [string]$BuildStep = "all", [switch]$IsAzurePipelineBuild = $false, [switch]$Help = $false ) +If ([String]::IsNullOrEmpty($VersionOfSDK)) { + $VersionOfSDK = $Env:XES_PACKAGEVERSIONNUMBER +} + +If ([String]::IsNullOrEmpty($VersionOfSDK)) { + $VersionOfSDK = "0.0.0" +} + $StartTime = Get-Date if ($Help) { From b63520858f96f714c2b24ad3119a5aba6b4eb962 Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Wed, 14 May 2025 12:16:35 -0700 Subject: [PATCH 007/117] build: stage the command palette as a separate artifact (#39422) This will ensure that the command palette package is copied to the artifact directory. If code signing was enabled, the final copied package will be the signed version. Minor build rule rearranging was required to collect the command palette package path for the staging step when signing was _disabled_. I did this solely so that we could verify the results in CI. --- .pipelines/v2/templates/job-build-project.yml | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/.pipelines/v2/templates/job-build-project.yml b/.pipelines/v2/templates/job-build-project.yml index d9d8da5773..d386ec15dd 100644 --- a/.pipelines/v2/templates/job-build-project.yml +++ b/.pipelines/v2/templates/job-build-project.yml @@ -399,13 +399,13 @@ jobs: **\UnitTests-FancyZones.dll !**\obj\** - - ${{ if eq(parameters.codeSign, true) }}: - - pwsh: |- - $Package = (Get-ChildItem -Recurse -Filter "Microsoft.CmdPal.UI_*.msix" | Select -First 1) - $PackageFilename = $Package.FullName - Write-Host "##vso[task.setvariable variable=CmdPalPackagePath]${PackageFilename}" - displayName: Locate the MSIX + - pwsh: |- + $Package = (Get-ChildItem -Recurse -Filter "Microsoft.CmdPal.UI_*.msix" | Select -First 1) + $PackageFilename = $Package.FullName + Write-Host "##vso[task.setvariable variable=CmdPalPackagePath]${PackageFilename}" + displayName: Locate the CmdPal MSIX + - ${{ if eq(parameters.codeSign, true) }}: - pwsh: |- & "$(MakeAppxPath)" unpack /p "$(CmdPalPackagePath)" /d "$(JobOutputDirectory)/CmdPalPackageContents" displayName: Unpack the MSIX for signing @@ -425,6 +425,8 @@ jobs: $PackageFilename = Join-Path $outDir.FullName (Split-Path -Leaf "$(CmdPalPackagePath)") & "$(MakeAppxPath)" pack /h SHA256 /o /p $PackageFilename /d "$(JobOutputDirectory)/CmdPalPackageContents" Copy-Item -Force $PackageFilename "$(CmdPalPackagePath)" + Remove-Item -Force -Recurse "$(JobOutputDirectory)/CmdPalPackageContents" -ErrorAction:Ignore + Remove-Item -Force -Recurse "$(JobOutputDirectory)/_appx" -ErrorAction:Ignore displayName: Re-pack the new CmdPal package after signing - template: steps-esrp-signing.yml @@ -447,6 +449,10 @@ jobs: batchSignPolicyFile: '$(build.sourcesdirectory)\.pipelines\ESRPSigning_DSC.json' ciPolicyFile: '$(build.sourcesdirectory)\.pipelines\CIPolicy.xml' + - pwsh: |- + Copy-Item -Verbose -Force "$(CmdPalPackagePath)" "$(JobOutputDirectory)" + displayName: Stage the final CmdPal package + - template: steps-build-installer.yml parameters: codeSign: ${{ parameters.codeSign }} From fce3c2d53739957022efb14600b3049837f7b6bf Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Wed, 14 May 2025 12:17:52 -0700 Subject: [PATCH 008/117] Move to TouchdownBuild task v5 (#39382) --- .pipelines/loc/loc.yml | 2 +- .../v2/templates/steps-fetch-and-prepare-localizations.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.pipelines/loc/loc.yml b/.pipelines/loc/loc.yml index 8d582c4830..cc4512c92e 100644 --- a/.pipelines/loc/loc.yml +++ b/.pipelines/loc/loc.yml @@ -25,7 +25,7 @@ steps: fetchDepth: 1 # Don't need a deep checkout for loc files! persistCredentials: true -- task: MicrosoftTDBuild.tdbuild-task.tdbuild-task.TouchdownBuildTask@3 +- task: MicrosoftTDBuild.tdbuild-task.tdbuild-task.TouchdownBuildTask@5 displayName: 'Touchdown Build - 37400, PRODEXT' inputs: teamId: 37400 diff --git a/.pipelines/v2/templates/steps-fetch-and-prepare-localizations.yml b/.pipelines/v2/templates/steps-fetch-and-prepare-localizations.yml index 30cf2b6f67..44f8c4b6dc 100644 --- a/.pipelines/v2/templates/steps-fetch-and-prepare-localizations.yml +++ b/.pipelines/v2/templates/steps-fetch-and-prepare-localizations.yml @@ -4,7 +4,7 @@ parameters: default: false steps: - - task: TouchdownBuildTask@3 + - task: TouchdownBuildTask@5 displayName: 'Download Localization Files -- PowerToys 37400' inputs: teamId: 37400 From 9bcb140af130da4bd55df3134ddba2f436df6965 Mon Sep 17 00:00:00 2001 From: Clint Rutkas Date: Wed, 14 May 2025 20:21:42 +0000 Subject: [PATCH 009/117] Validate names for invalid for C# namespaces in cmdpal (#39401) ## Summary of the Pull Request The Command Palette allows users to create extensions with dashes in their names, which is invalid for C# namespaces. This causes projects to fail during build because the name cannot be used as a valid namespace. ## Changes - Updated the validation regex in `NewExtensionForm.cs` to only allow valid C# identifiers - Now only allows names starting with a letter or underscore, followed by letters, numbers, or underscores - Explicitly prevents dashes, spaces, and other special characters - Improved the error message to clearly explain the requirements for valid C# identifiers ## Before Previously, the input only validated that no spaces were used: ```csharp "regex": "^[^\\s]+$" ``` ## After Now the input properly validates for C# namespace compatibility: ```csharp "regex": "^[a-zA-Z_][a-zA-Z0-9_]*$" ``` This ensures that users cannot create extension projects with names that would fail to build.![image](https://github.com/user-attachments/assets/c2fb5108-b32b-4411-84a8-45ef0c621372) ## PR Checklist - [ ] **Closes:** https://github.com/microsoft/PowerToys/issues/38522 --- .../Microsoft.CmdPal.UI.ViewModels/Commands/NewExtensionForm.cs | 2 +- .../Properties/Resources.Designer.cs | 2 +- .../Microsoft.CmdPal.UI.ViewModels/Properties/Resources.resx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Commands/NewExtensionForm.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Commands/NewExtensionForm.cs index aca45f3494..14cd4a0e16 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Commands/NewExtensionForm.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Commands/NewExtensionForm.cs @@ -55,7 +55,7 @@ internal sealed partial class NewExtensionForm : NewExtensionFormBase "errorMessage": {{FormatJsonString(Properties.Resources.builtin_create_extension_name_required)}}, "id": "ExtensionName", "placeholder": "ExtensionName", - "regex": "^[^\\s]+$" + "regex": "^[a-zA-Z_][a-zA-Z0-9_]*$" }, { "type": "TextBlock", diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Properties/Resources.Designer.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Properties/Resources.Designer.cs index 5552304ca2..5c4d961aa7 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Properties/Resources.Designer.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Properties/Resources.Designer.cs @@ -178,7 +178,7 @@ namespace Microsoft.CmdPal.UI.ViewModels.Properties { } /// - /// Looks up a localized string similar to Extension name is required, without spaces. + /// Looks up a localized string similar to Extension name is required and must be a valid C# identifier (start with a letter or underscore, followed by letters, numbers, or underscores). /// public static string builtin_create_extension_name_required { get { diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Properties/Resources.resx b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Properties/Resources.resx index 4dc5a1539f..a1755ab097 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Properties/Resources.resx +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Properties/Resources.resx @@ -195,7 +195,7 @@ Extension name - Extension name is required, without spaces + Extension name is required and must be a valid C# identifier (start with a letter or underscore, followed by letters, numbers, or underscores) Display name From 0bb15f4e2c0e3e385049869673fd4efd9432b094 Mon Sep 17 00:00:00 2001 From: yaqingmi Date: Thu, 15 May 2025 06:55:19 +0800 Subject: [PATCH 010/117] 0.91 changelog (#39266) * Update version to 0.91 Update version to 0.91 * Some PRs are still not included. Some PRs are still not included. * Add some PRs * Add more PRs * Add more PRs * Add two more PRs * Add some PRs * Add one more PR * Add all PRs up to this point, except for some documentation-related ones. * Add the Highlights part * Overall edits * Add PRs about Doc changes * Clean up the highlights section * Update MD5 * Changed highlights and removed reg preview line item * Put reg preview item back and updated highlights --------- Co-authored-by: Kayla Cinnamon --- README.md | 190 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 131 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index 1a0d80dc00..07fea61da1 100644 --- a/README.md +++ b/README.md @@ -35,19 +35,19 @@ Microsoft PowerToys is a set of utilities for power users to tune and streamline Go to the [Microsoft PowerToys GitHub releases page][github-release-link] and click on `Assets` at the bottom to show the files available in the release. Please use the appropriate PowerToys installer that matches your machine's architecture and install scope. For most, it is `x64` and per-user. -[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.91%22 -[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.90%22 -[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.90.0/PowerToysUserSetup-0.90.0-x64.exe -[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.90.0/PowerToysUserSetup-0.90.0-arm64.exe -[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.90.0/PowerToysSetup-0.90.0-x64.exe -[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.90.0/PowerToysSetup-0.90.0-arm64.exe +[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.92%22 +[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.91%22 +[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.90.0/PowerToysUserSetup-0.91.0-x64.exe +[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.90.0/PowerToysUserSetup-0.91.0-arm64.exe +[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.90.0/PowerToysSetup-0.91.0-x64.exe +[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.90.0/PowerToysSetup-0.91.0-arm64.exe | Description | Filename | sha256 hash | |----------------|----------|-------------| -| Per user - x64 | [PowerToysUserSetup-0.90.0-x64.exe][ptUserX64] | 2A6036F5B2D454084E55816C306E1E57EF1D14C916691CBDA42B469797605CE0 | -| Per user - ARM64 | [PowerToysUserSetup-0.90.0-arm64.exe][ptUserArm64] | AB2E4DC87A9D764BE897C5170E2890E174C89CA912A1916FA3AE1E427536EA4A | -| Machine wide - x64 | [PowerToysSetup-0.90.0-x64.exe][ptMachineX64] | 12801C44F43D0CC61E90DF1EFDC40E4F3C88341E0199D5B20791042D9B173DCF | -| Machine wide - ARM64 | [PowerToysSetup-0.90.0-arm64.exe][ptMachineArm64] | 2998007C8FCD7BD2770767C6502AAA2CC75B85EC30DE62986EC7005EB0014EDB | +| Per user - x64 | [PowerToysUserSetup-0.91.0-x64.exe][ptUserX64] | 190DD702EDE2D3AC27A253DF8BC2416B1AF05E6594FF25CABEE844E6D3C8CCB0 | +| Per user - ARM64 | [PowerToysUserSetup-0.91.0-arm64.exe][ptUserArm64] | BE6C964C40147B5F7838E51A13837347756CC45E6AC5BC0DD11AF9AF605ABDCD | +| Machine wide - x64 | [PowerToysSetup-0.91.0-x64.exe][ptMachineX64] | 2308D896D9A66C56B98AC8B3CE9B7C945C7A2315551E36C118C7ECAC4A6D05C2 | +| Machine wide - ARM64 | [PowerToysSetup-0.91.0-arm64.exe][ptMachineArm64] | 28BD1FEFA22C52279C6B600E677B425B014D1F9190EA449D6C63FC2702092DA3 | This is our preferred method. @@ -93,92 +93,164 @@ For guidance on developing for PowerToys, please read the [developer docs](/doc/ Our [prioritized roadmap][roadmap] of features and utilities that the core team is focusing on. -### 0.90 - March 2025 Update +### 0.91 - May 2025 Update In this release, we focused on new features, stability, and automation. **✨Highlights** -![Gif for Command Palette](doc/images/overview/CmdPal_Hero.gif) + - We focused on greatly improving Command Palette's performance and fixing a large amount of bugs. Some new features we've added are: + - Added the ability for Command Palette to search any file using a fallback command. + - Added the ability to make the Command Palette global hotkey a low-level keyboard hook. + - Added open URL fallback command for the WebSearch extension, enabling users to directly open URLs in the browser from Command Palette. + - You can now define custom formats in the Date and Time plugins of PT Run and Command Palette. Thanks [@htcfreek](https://github.com/htcfreek)! -- New module: Command Palette ("CmdPal") - Created as the evolution of PowerToys Run with extensibility at the forefront, Command Palette is a quick launcher with a richer display and additional capabilities without sacrificing performance, allowing you to start anything with the shortcut **Win+Alt+Space**! Thanks [@zadjii-msft](https://github.com/zadjii-msft), [@niels9001](https://github.com/niels9001), [@michael-hawker](https://github.com/michael-hawker), [@joadoumie](https://github.com/joadoumie), [@plante-msft](https://github.com/plante-msft), [@ethanfangg](https://github.com/ethanfangg) and [@krschau](https://github.com/krschau)! - - Enhanced the Color Picker by switching from WPF UI to .NET WPF, resulting in improved themes and visual consistency across different modes. Thanks [@mantaionut](https://github.com/mantaionut)! Thanks [@Jay-o-Way](https://github.com/Jay-o-Way) and [@niels9001](https://github.com/niels9001) for helping with the review! - - Added the ability to delete files directly from Peek, enhancing file management efficiency. Thanks [@daverayment](https://github.com/daverayment) and thanks [@htcfreek](https://github.com/htcfreek) for the review! - - Added support for variables in template filenames, enabling dynamic elements like date components and environment variables for enhanced customization in New+. Thanks [@cgaarden](https://github.com/cgaarden)! +### Advanced Paste + + - Fixed an issue where Advanced Paste failed to create the OCR engine for certain English language tags (e.g., en-CA) by initializing the OCR engine with the user profile language. Thanks [@cryolithic](https://github.com/cryolithic)! ### Color Picker - - Replaced WPF UI with .NET WPF for the Color Picker, enhancing compatibility and improving theme support. Thanks [@mantaionut](https://github.com/mantaionut)! Thanks [@Jay-o-Way](https://github.com/Jay-o-Way) and [@niels9001](https://github.com/niels9001) for helping with the review! + - Fixed an issue where a resource leak caused hangs or crashes by properly disposing of the Graphics object. Thanks [@dcog989](https://github.com/dcog989)! + - Fixed an issue where Color Picker exited on Backspace keypress by ensuring it only closes when focused and aligning Escape/Backspace behavior. Thanks [@PesBandi](https://github.com/PesBandi)! + - Added support for Oklab and Oklch color formats in Color Picker. Thanks [@lemonyte](https://github.com/lemonyte)! + +### Command Not Found + + - Updated the WinGet Command Not Found script to only enable the experimental features if they actually exist. ### Command Palette -- Introduced the Windows Command Palette ("CmdPal"), the next iteration of PowerToys Run, designed with extensibility at its core. CmdPal includes features such as searching for installed apps, shell commands, files and WinGet package installation. This module aims to provide a more powerful and flexible launcher experience. Thanks [@zadjii-msft](https://github.com/zadjii-msft), [@niels9001](https://github.com/niels9001), [@michael-hawker](https://github.com/michael-hawker), [@joadoumie](https://github.com/joadoumie), [@plante-msft](https://github.com/plante-msft), and the whole team! - -### FancyZones - - - Fixed a bug where deleting a layout resulted in incorrect data being written to the JSON file. - - Fixed a bug where layout hotkeys were displayed incorrectly, ensuring the hotkey list does not include invalid entries. - - Fixed an issue where the "None" option was missing in the editor layout. + - Updated bug template to include Command Palette module. + - Fixed an issue where the toast window was not scaled for DPI, causing layout issues under display scaling. + - Fixed an issue where Up/Down keyboard navigation didn't move selection when caret was at position 0, and add continuous navigation like PT Run v1. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! + - Updated the Time and Date extension code to simplify it and improve clarity. + - Fixed an issue where capitalization in the command causes failure when trying to go to the mouse pointer, resolved by adjusting the command to lowercase. + - Added open URL fallback command for the WebSearch extension, enabling users to directly open URLs in the browser from Command Palette. Thanks [@htcfreek](https://github.com/htcfreek)! + - Added setting to enable/disable system tray icon in CmdPal and align terminology with Windows 11. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! + - Fixed an alias update issue by removing the old alias when a new one is set. + - Resolved GitHub casing conflict by migrating Exts and exts into a new ext directory, ensuring consistent structure across platforms and preventing path fragmentation. + - Fix an issue where the 'Create New Extension' command generated empty file names. + - Added the ability to make the global hotkey a low-level keyboard hook. + - Added support for JUMBO thumbnails, enabling access to high-resolution icons. + - Fixed crashes when CmdPal auto-hid itself while an MSAL dialog was opened, by preventing CmdPal from hiding if it's disabled. + - Added support for immediately selecting search text when a page is loaded. + - Fixed a bug where extension settings pages failed to reload on reopen by updating the settings form when extension settings are saved. + - Fixed an issue where the Command Palette failed to launch from the runner. + - Refactored and ported the PowerToys Run v1 calculator logic into Command Palette, added settings support, and improved fallback behavior. + - Re-added support for list item keyboard shortcuts. + - Enhanced accessibility in Command Palette by adding proper labels, refining animations, improving localization, and fixed a11y related issues. + - Ported custom format support to the Time and Date plugin, reordered and cleaned up settings, improved error messaging, and fixed edge-case crashes for more robust and user-friendly behavior. Thanks [@htcfreek](https://github.com/htcfreek)! + - Added fallback item for system command. + - Fixed a bug in Windows System Command where the key prompt incorrectly displayed "Empty" for the "Open Recycle Bin" action. Thanks [@jironemo](https://github.com/jironemo)! + - Fixed an issue where the 'more commands' list showed commands that shouldn't be visible. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! + - Fixed an issue where the details view in Command Palette displayed an oversized icon and misaligned text, aligning it with Windows Search behavior. + - Fixed a bug where empty screen content and command bar commands were cut off when using long labels, ensuring proper layout and visibility. + - Improved CmdPal’s WinGet integration by fixing version display for installed packages, enabling updates with icons, and migrating the preview winget API to a stable version. + - Fixed a bug where commands for ContentPage didn't update until after exit, by ensuring context menus are fully initialized when they change. + - Added fallback support to the TimeDate extension, enabling direct date/time queries without pre-selecting the command. + - Added import of Common.Dotnet.AotCompatibility.props across multiple CmdPal project files to enhance AOT compilation support. + - Fixed a crash in CmdPal settings caused by a null HotKey when settings.json is missing or lacks a defined hotkey. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! + - Added support for filterable, nested context menus in CmdPal, including a search box to maintain focus behavior. + - Refactored CmdPal classes to improve JSON serialization and introduced new serialization contexts for better performance and maintainability. + - Added support for ahead-of-time (AoT) compilation. + - Added retry mechanism for CmdPal launch. + - Removed some unused files from CmdPal.Common to simplify codebase and facilitate marking it as AoT-compatible. + - Fixed a bug where a race condition in the update of SearchText caused the cursor in the input box to automatically jump to the end of the line, ensuring SearchText is only updated after it has actually been changed. + - Added support for searching any file in fallback command. + - Cleaned up AoT-related code to prevent duplicate operations during testing. + - Reduced CmdPal load time by parallelizing extension startup and adding timeouts to prevent misbehaving extensions from blocking others. + - Enhanced UI behavior by dismissing the details pane when the list gets emptied, avoiding inconsistent visual states. + - Added support to unset the fallback command in CmdPal when no matching command is found, ensuring cleaner reload behavior. + - Fixed a leak in the CmdPal extension template by addressing improper ComServer use. + - Prevented CmdPal window from maximizing on title bar double-click to maintain intended window behavior. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! + - Fixed an issue where the Settings UI launched too small by making window dimensions DPI-aware and enforcing minimum width and height using WinUIEx. + - Fixed white flash and one-time animation issues in CmdPal by cloaking the window instead of hiding it. + - Fixed a bug where all extension settings were fetched on startup by lazy-loading extension settings, reducing initialization overhead. + - Added support for protecting CmdPal from crashes on Adaptive Card parse failure. + - Replaced shell:AppsFolder with URI activation in CmdPal to improve reliability. + - Added ability to open CmdPal settings from PowerToys Settings. + - Added ability for CmdPal to observe and dynamically update extension details by tracking property changes on the selected item. + - Bumped the toolkit version used in the CmdPal extension template to 0.2.0. ### Image Resizer - - Fixed warnings in ImageResizer regarding the use of variables "shellItem" and "itemName" without being initialized. + - Fixed an issue where deleting an Image Resizer preset removed the wrong preset. -### Mouse Without Borders +### Keyboard Manager - - Enhanced the logger to properly track the file path for easier debugging. - - Refactored the "Common" class into distinct individual classes to enhance maintainability, and updated all references and unit tests to reflect these changes. Thanks [@mikeclayton](https://github.com/mikeclayton) for this! + - Fixed an issue where a modifier key, when set without specifying left or right, would get stuck due to incorrect key handling, by tracking the pressed keys and sending the correct key accordingly. Thanks [@mantaionut](https://github.com/mantaionut)! -### New+ +### PowerRename - - Added support for variables in template filenames, including date/time components, parent folder name, and environment variables. Thanks [@cgaarden](https://github.com/cgaarden)! - -### Peek - - - Added the ability to delete the file currently being previewed in Peek, including navigation updates and handling for deleted items. Thanks [@daverayment](https://github.com/daverayment) and thanks [@htcfreek](https://github.com/htcfreek) for your help reviewing this! + - Enhanced PowerRename's time formatting capabilities by adding 12-hour time format patterns with AM/PM support. Thanks [@bitmap4](https://github.com/bitmap4)! ### PowerToys Run - - Fixed an issue where duplicated applications were shown by ensuring the shell link helper opens .ink files non-exclusively and correctly retrieves the "FullPath". Thanks [@htcfreek](https://github.com/htcfreek) and [@davidegiacometti](https://github.com/davidegiacometti) for review! - - Fixed an issue where applying round corners on Windows 11 build 22000 caused crashes. - - Async the OnRename method to unblock the thread. Thanks [@davidegiacometti](https://github.com/davidegiacometti) for review! - - Added support for using `sq` instead of `^2` in the Unit Converter. Thanks [@PesBandi](https://github.com/PesBandi)! + - Added support for custom formats in the "Time and Date" plugin and improves error messages for invalid input formats. Thanks [@htcfreek](https://github.com/htcfreek)! + - Fix two crashes: one for WFT on very early dates and another for calculating the week of the month on very late dates (e.g., 31.12.9999), and reorder UI settings. Thanks [@htcfreek](https://github.com/htcfreek)! + - Fix an issue where capitalization in the command causes failure when trying to go to the mouse pointer, resolved by adjusting the command to lowercase. + - Added version details to plugin error messages for 'Loading error' and 'Init error'. Thanks [@htcfreek](https://github.com/htcfreek)! + - Enhanced result model by adding support for preventing usage-based ordering, giving plugin developers greater control over sorting behavior. Thanks [@CoreyHayward](https://github.com/CoreyHayward) and [@htcfreek](https://github.com/htcfreek)! + +### Quick Accent + + - Updated the letter mapping in GetDefaultLetterKeyEPO, replacing "ǔ" with "ŭ" for the VK_U key to accurately reflect Esperanto phonetics. Thanks [@OlegKharchevkin](https://github.com/OlegKharchevkin)! + - Fixed an issue where Quick Accent did not work properly when using the on-screen keyboard. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! + +### Registry Preview + + - Enhanced Registry Preview to support pasting registry keys and values without manually writing the file header, and added a new button for resetting the app. Thanks [@htcfreek](https://github.com/htcfreek)! ### Settings - - Disabled the spell check feature in the text boxes of plugin settings for PowerToys Run. Thanks [@htcfreek](https://github.com/htcfreek)! - - Fixed an issue where InfoBars for release notes errors were not displayed properly, and added a retry button. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! + - Fix an issue where the Settings app randomly showed a blank icon in the taskbar by deferring icon assignment until the window is activated. + - Added the ability to maximize the "What's New" window for a more comfortable reading experience. ### Workspaces - - Fixed an issue where some minimized packaged apps (e.g., Microsoft ToDo, Settings) were not snapshotted. + - Fixed bugs where Steam games were not captured or launched correctly by updating window filtering and integrating Steam URL protocol handling. ### Documentation - - Added the FirefoxBookmark plugin to the list of Third-Party plugins for PowerToys Run. Thanks [@8LWXpg](https://github.com/8LWXpg)! - - Added the SVGL third-party plugin to PowerToys Run, enabling users to search, browse, and copy SVG logos. Thanks [@SameerJS6](https://github.com/SameerJS6)! - - Added Monaco usage for the Registry Preview. + - Added QuickNotes to the third-party plugins documentation for PowerToys Run. Thanks [@ruslanlap](https://github.com/ruslanlap)! + - Added Weather and Pomodoro plugins to the PowerToys Run third-party plugin documentation. Thanks [@ruslanlap](https://github.com/ruslanlap)! + - Added the Linear plugin to PowerToys Run's third-party plugin documentation. Thanks [@vednig](https://github.com/vednig)! + - Fixed formatting issues in documentation files and updated contributor and team member information. Thanks [@DanielEScherzer](https://github.com/DanielEScherzer) and [@RokyZevon](https://github.com/RokyZevon)! ### Development - - Updated WinGet configuration file location and extension. Thanks [@mdanish-kh](https://github.com/mdanish-kh)! - - Removed the Markdown file bypass to ensure CI runs for commits that only update Markdown files. - - Fixed an issue where the default generated file path exceeded the length limit of 260 characters for EnvironmentVariablesUILib.csproj, causing build failures. - - Upgraded WindowsAppSDK to 1.6.250205002 and CsWinRT to 2.2.0. Thanks [@htcfreek](https://github.com/htcfreek) for review! - - Upgraded XamlStyler to 3.2501.8 and dotnet-consolidate to 4.2.0. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! - - Updated .NET Packages from 9.0.2 to 9.0.3. - - Optimized the UI Test Automation Framework and added UI test cases for the Hosts File Editor module. - - Added fuzz testing for RegistryPreview. - - Added new UI tests for the FancyZones editor, including tests for creating, duplicating, editing, and deleting layouts. - - Added telemetry code to measure the module editor open time and evaluate the benefits of applying AOT. + - Updated GitHub Action to install .NET 9 for MSStore release support. + - Updated version placeholder in bug_report.yml to prevent incorrect v0.70.0 versioning in issue reports. + - Updated GitHub Action to upgrade actions/setup-dotnet from version 3 to version 4 for MSStore release. + - Added securityContext to WinGet configuration files, allowing invocation from user context and prompting a single UAC for elevated resources in a separate process. Thanks [@mdanish-kh](https://github.com/mdanish-kh)! + - Changed log file extensions from .txt to .log to support proper file associations and tooling compatibility, and added logs for Workspace. Thanks [@benwa](https://github.com/benwa)! + - Upgraded testing framework dependencies and aligned package versions across components. + - Upgraded dependencies to fix vulnerabilities. + - Enhanced repository security by pinning GitHub Actions and Docker tags to immutable full-length commits and integrating automated dependency vulnerability scanning via Dependency Review Workflow. Thanks [@Nick2bad4u](https://github.com/Nick2bad4u)! + - Upgraded Boost dependencies to a newer version. + - Upgraded toolkit to the latest version, suppressed AoT-related warnings. + - Fixed an issue where missing signing for newly added files caused build failures. + - Update release pipeline to prevent publishing private symbols for 100 years. + - Introduced fuzzing for PowerRename to improve reliability and added setup guidance for extending fuzzing to other C++ modules. + - Added centralized pre-creation of generated folders for all .csproj projects to prevent build failures. + - Updated WinAppSDK to the latest 1.7 version. + - Upgraded Boost dependencies to the latest version for the PowerRename Fuzzing project. + - Updated the ADO area path in tsa.json to resolve TSA pipeline errors caused by a deprecated path. + - Initiated AoT support for CmdPal with foundational work in progress. + +### Tool/General + - Added support for automating bug report creation by generating a pre-filled GitHub issue URL with system and diagnostic information. Thanks [@donlaci](https://github.com/donlaci)! + - Added scripts to locally build the installer, ensuring the CmdPal can also be launched in a local environment. + - Removed export PFX logic to eliminate hardcoded password usage and resolve PSScriptAnalyzer security warning. + - Added PowerShell script and CI integration to enforce consistent use of Common.Dotnet.CsWinRT.props across all C# projects under the src folder. + +### What is being planned for version 0.92 - -### What is being planned for version 0.91 +For [v0.92][github-next-release-work], we'll work on the items below: -For [v0.91][github-next-release-work], we'll work on the items below: - - - New module: File Actions Menu + - Continued Command Palette polish - New UI Automation tests - Working on installer upgrades - Upgrading Keyboard Manager's editor UI From 1837dc5ee629a947b0f76606dd558839de19f071 Mon Sep 17 00:00:00 2001 From: Kayla Cinnamon Date: Wed, 14 May 2025 19:43:33 -0400 Subject: [PATCH 011/117] Fix download links in README (#39425) --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 07fea61da1..7dd5a35cd4 100644 --- a/README.md +++ b/README.md @@ -37,10 +37,10 @@ Go to the [Microsoft PowerToys GitHub releases page][github-release-link] and cl [github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.92%22 [github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.91%22 -[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.90.0/PowerToysUserSetup-0.91.0-x64.exe -[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.90.0/PowerToysUserSetup-0.91.0-arm64.exe -[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.90.0/PowerToysSetup-0.91.0-x64.exe -[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.90.0/PowerToysSetup-0.91.0-arm64.exe +[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.91.0/PowerToysUserSetup-0.91.0-x64.exe +[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.91.0/PowerToysUserSetup-0.91.0-arm64.exe +[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.91.0/PowerToysSetup-0.91.0-x64.exe +[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.91.0/PowerToysSetup-0.91.0-arm64.exe | Description | Filename | sha256 hash | |----------------|----------|-------------| From 898e7c635263a75e21ec156a274a5c4e2754781a Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Thu, 15 May 2025 14:47:03 -0700 Subject: [PATCH 012/117] build: strong name sign the Extension Toolkit (#39469) Strong-name signing embeds publisher identity into the signature of a .NET assembly. This is required if *any other* strong name signed project wants to take a dependency on it. To make this work, we need to delay-sign it with a public key (.snk file)--e.g. say we are going to sign it, but not actually sign it--to give it an identity and then later submit it to ESRP for final signing. The snk file does not contain any private material. Some minor changes were required to build properly: - `InternalsVisibleTo` requires a PublicKeyToken, but we aren't using it in the SDK build so it's fine to just leave it out. - I had to mark a class `sealed` and I can only guess it's because strong named assemblies have more guarantees? --- .github/actions/spell-check/expect.txt | 1 + .pipelines/272MSSharedLibSN2048.snk | Bin 0 -> 288 bytes .pipelines/ESRPSigning_sdk.json | 59 +++++++++++++++++- .../JsonSerializationContext.cs | 2 +- .../MatchOption.cs | 2 - .../MatchResult.cs | 2 - ...t.CommandPalette.Extensions.Toolkit.csproj | 6 ++ .../extensionsdk/nuget/BuildSDKHelper.ps1 | 4 ++ 8 files changed, 70 insertions(+), 6 deletions(-) create mode 100644 .pipelines/272MSSharedLibSN2048.snk diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index bdae0338be..f9d35feab1 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -1535,6 +1535,7 @@ SMALLICON smartphone SMTO SNAPPROCESS +snk snwprintf softline SOURCECLIENTAREAONLY diff --git a/.pipelines/272MSSharedLibSN2048.snk b/.pipelines/272MSSharedLibSN2048.snk new file mode 100644 index 0000000000000000000000000000000000000000..bd766f84a23ac0323c0589df14827e0644f6a6ae GIT binary patch literal 288 zcmV+*0pI=rBme*mfB*m#0RR970ssI2Bme+XQ$aBR2mk;90097ns?fghXpI}0N)347 z{VDt;tTgRCI>Y;$Jq$=VYp67;hyXPP3W!Lu*sb-BXAaT{6uvfrsFBIYz#i`^vM6#? zd^x@VuMTW-NL_sW8d2YgN7HQUshE)lwTixZ=A-8t0qtwthHw&yJ_{O6HLp+enc>H;SZF)np*8VQkMZhY=?#0CI&6piJ=rt}<6RP#`KS4*d|kC{4?MzJ m(H@s~`ArjxJnB=qs52ZcZOe@=sZJQb5o7-mDk9t2Ekc^_F@YHX literal 0 HcmV?d00001 diff --git a/.pipelines/ESRPSigning_sdk.json b/.pipelines/ESRPSigning_sdk.json index 066acf9e4e..e2e2db7701 100644 --- a/.pipelines/ESRPSigning_sdk.json +++ b/.pipelines/ESRPSigning_sdk.json @@ -4,9 +4,66 @@ "SignBatches": [ { "MatchedPath": [ - "Microsoft.CommandPalette.Extensions.dll", "Microsoft.CommandPalette.Extensions.Toolkit.dll" ], + "SigningInfo": { + "Operations": [ + { + "KeyCode": "CP-233904-SN", + "OperationSetCode": "StrongNameSign", + "ToolName": "sign", + "ToolVersion": "1.0", + "Parameters": [] + }, + { + "KeyCode": "CP-233904-SN", + "OperationSetCode": "StrongNameVerify", + "ToolName": "sign", + "ToolVersion": "1.0", + "Parameters": [] + }, + { + "KeyCode": "CP-230012", + "OperationSetCode": "SigntoolSign", + "Parameters": [ + { + "parameterName": "OpusName", + "parameterValue": "Microsoft" + }, + { + "parameterName": "OpusInfo", + "parameterValue": "http://www.microsoft.com" + }, + { + "parameterName": "FileDigest", + "parameterValue": "/fd \"SHA256\"" + }, + { + "parameterName": "PageHash", + "parameterValue": "/NPH" + }, + { + "parameterName": "TimeStamp", + "parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256" + } + ], + "ToolName": "sign", + "ToolVersion": "1.0" + }, + { + "KeyCode": "CP-230012", + "OperationSetCode": "SigntoolVerify", + "Parameters": [], + "ToolName": "sign", + "ToolVersion": "1.0" + } + ] + } + }, + { + "MatchedPath": [ + "Microsoft.CommandPalette.Extensions.dll" + ], "SigningInfo": { "Operations": [ { diff --git a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/JsonSerializationContext.cs b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/JsonSerializationContext.cs index 6d92cdc146..6a0dde88cc 100644 --- a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/JsonSerializationContext.cs +++ b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/JsonSerializationContext.cs @@ -16,6 +16,6 @@ namespace Microsoft.CommandPalette.Extensions.Toolkit; [JsonSerializable(typeof(List))] [JsonSerializable(typeof(Dictionary), TypeInfoPropertyName = "Dictionary")] [JsonSourceGenerationOptions(UseStringEnumConverter = true, WriteIndented = true)] -internal partial class JsonSerializationContext : JsonSerializerContext +internal sealed partial class JsonSerializationContext : JsonSerializerContext { } diff --git a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/MatchOption.cs b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/MatchOption.cs index 5f56c5a5b0..9f740e4ade 100644 --- a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/MatchOption.cs +++ b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/MatchOption.cs @@ -5,8 +5,6 @@ using System; using System.Runtime.CompilerServices; -[assembly: InternalsVisibleTo("Microsoft.Plugin.Program.UnitTests")] - namespace Microsoft.CommandPalette.Extensions.Toolkit; public partial class MatchOption diff --git a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/MatchResult.cs b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/MatchResult.cs index ad61733f9e..3848065f25 100644 --- a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/MatchResult.cs +++ b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/MatchResult.cs @@ -4,8 +4,6 @@ using System.Runtime.CompilerServices; -[assembly: InternalsVisibleTo("Microsoft.Plugin.Program.UnitTests")] - namespace Microsoft.CommandPalette.Extensions.Toolkit; public partial class MatchResult diff --git a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/Microsoft.CommandPalette.Extensions.Toolkit.csproj b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/Microsoft.CommandPalette.Extensions.Toolkit.csproj index 8f3cdaed91..4ceae15953 100644 --- a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/Microsoft.CommandPalette.Extensions.Toolkit.csproj +++ b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/Microsoft.CommandPalette.Extensions.Toolkit.csproj @@ -15,6 +15,12 @@ None + + true + true + $(MSBuildThisFileDirectory)..\..\..\..\..\.pipelines\272MSSharedLibSN2048.snk + + Microsoft.CommandPalette.Extensions $(OutDir) diff --git a/src/modules/cmdpal/extensionsdk/nuget/BuildSDKHelper.ps1 b/src/modules/cmdpal/extensionsdk/nuget/BuildSDKHelper.ps1 index 52e2f17586..8270e7bb4a 100644 --- a/src/modules/cmdpal/extensionsdk/nuget/BuildSDKHelper.ps1 +++ b/src/modules/cmdpal/extensionsdk/nuget/BuildSDKHelper.ps1 @@ -69,6 +69,10 @@ if (($BuildStep -ieq "all") -Or ($BuildStep -ieq "build")) { ("/p:VersionNumber="+$VersionOfSDK) ) + if ($IsAzurePipelineBuild) { + $msbuildArgs += "/p:CIBuild=true" + } + & $msbuildPath $msbuildArgs } } From 75121ca7f3491f769423ba2c141934d6b5402de8 Mon Sep 17 00:00:00 2001 From: Gordon Lam <73506701+yeelam-gordon@users.noreply.github.com> Date: Thu, 15 May 2025 14:47:40 -0700 Subject: [PATCH 013/117] Fix RunAsAdmin For CmdPal when PowerToys is running as Admin (#39448) ## Summary of the Pull Request Since we change the launch method by this PR #39269 , we will start cmdpal as admin too if powertoys run as admin. The fix is leveraging explorer (which will not run as admin) to start the cmdpal Moreover, without this fix, some of extension cannot be "loaded" when cmdpal run as admin, e.g. winget will be missing, and my own new extension developed by myself will not be loaded successful as well. --- src/modules/cmdpal/CmdPalModuleInterface/dllmain.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/cmdpal/CmdPalModuleInterface/dllmain.cpp b/src/modules/cmdpal/CmdPalModuleInterface/dllmain.cpp index 48bfcd1411..655bbc9435 100644 --- a/src/modules/cmdpal/CmdPalModuleInterface/dllmain.cpp +++ b/src/modules/cmdpal/CmdPalModuleInterface/dllmain.cpp @@ -217,7 +217,7 @@ public: CmdPal::m_enabled.store(true); std::wstring packageName = L"Microsoft.CommandPalette"; - std::wstring launchPath = L"x-cmdpal://background"; + std::wstring launchPath = L"explorer.exe x-cmdpal://background"; #ifdef IS_DEV_BRANDING packageName = L"Microsoft.CommandPalette.Dev"; #endif @@ -348,4 +348,4 @@ std::atomic CmdPal::m_launched{ false }; extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create() { return new CmdPal(); -} \ No newline at end of file +} From 0e9c5a82dd4069353611ab56d0a86e4599c3ded8 Mon Sep 17 00:00:00 2001 From: Gordon Lam <73506701+yeelam-gordon@users.noreply.github.com> Date: Fri, 16 May 2025 04:11:57 -0700 Subject: [PATCH 014/117] Fix Cmdpal launch without admin mode - both runner and setting. (#39494) * Change to path and args * Fix both Setting launch and runner launch --- src/modules/cmdpal/CmdPalModuleInterface/dllmain.cpp | 12 +++++++----- .../SettingsXAML/Views/CmdPalPage.xaml.cs | 11 ++++++----- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/modules/cmdpal/CmdPalModuleInterface/dllmain.cpp b/src/modules/cmdpal/CmdPalModuleInterface/dllmain.cpp index 655bbc9435..9b9ce7ec56 100644 --- a/src/modules/cmdpal/CmdPalModuleInterface/dllmain.cpp +++ b/src/modules/cmdpal/CmdPalModuleInterface/dllmain.cpp @@ -217,7 +217,9 @@ public: CmdPal::m_enabled.store(true); std::wstring packageName = L"Microsoft.CommandPalette"; - std::wstring launchPath = L"explorer.exe x-cmdpal://background"; + // Launch CmdPal as normal user using explorer + std::wstring launchPath = L"explorer.exe"; + std::wstring launchArgs = L"x-cmdpal://background"; #ifdef IS_DEV_BRANDING packageName = L"Microsoft.CommandPalette.Dev"; #endif @@ -268,13 +270,13 @@ public: if (!firstEnableCall) { Logger::trace("Not first attempt, try to launch"); - LaunchApp(launchPath, L"", false /*no elevated*/, false /*error pop up*/); + LaunchApp(launchPath, launchArgs, false /*no elevated*/, false /*error pop up*/); } else { // If first time enable, do retry launch. Logger::trace("First attempt, try to launch"); - std::thread launchThread(&CmdPal::RetryLaunch, launchPath); + std::thread launchThread(&CmdPal::RetryLaunch, launchPath, launchArgs); launchThread.detach(); } @@ -289,14 +291,14 @@ public: CmdPal::m_enabled.store(false); } - static void RetryLaunch(std::wstring path) + static void RetryLaunch(std::wstring path, std::wstring cmdArgs) { const int base_delay_milliseconds = 1000; int max_retry = 9; // 2**9 - 1 seconds. Control total wait time within 10 min. int retry = 0; do { - auto launch_result = LaunchApp(path, L"", false, retry < max_retry); + auto launch_result = LaunchApp(path, cmdArgs, false, retry < max_retry); if (launch_result) { Logger::info(L"CmdPal launched successfully after {} retries.", retry); diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/CmdPalPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/CmdPalPage.xaml.cs index 275d5ef050..a9a016b80e 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/CmdPalPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/CmdPalPage.xaml.cs @@ -34,7 +34,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views ViewModel.RefreshEnabledState(); } - private void LaunchApp(string appPath) + private void LaunchApp(string appPath, string args) { try { @@ -43,7 +43,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views var processStartInfo = new ProcessStartInfo { FileName = appPath, - Arguments = string.Empty, + Arguments = args, WorkingDirectory = dir, UseShellExecute = true, Verb = "open", @@ -64,9 +64,10 @@ namespace Microsoft.PowerToys.Settings.UI.Views private void CmdPalSettingsDeeplink_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e) { - // Launch CmdPal settings window - string launchPath = "x-cmdpal://settings"; - LaunchApp(launchPath); + // Launch CmdPal settings window as normal user using explorer + string launchPath = "explorer.exe"; + string launchArgs = "x-cmdpal://settings"; + LaunchApp(launchPath, launchArgs); } } } From c9656754bd97b5b37bc7e69981ac29bcb3de8d81 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 16 May 2025 10:47:44 -0500 Subject: [PATCH 015/117] CmdPal: fix a perf regression loading pages (#39415) This was especially noticable with the icons extension. Turns out in #39051, when I was experimenting with getting AoT clean, i accidentally called this twice. Then we actually commited that straight up. This PR reverts that. It also moves a similar case where we were initializing all the tags on the UI thread. That's wrong too - we need to fetch properties off the UI thread, then update the list on the UI thread. Closes nothing, I didn't file this yet. --- .../ListItemViewModel.cs | 16 ++++++++-------- .../ShellViewModel.cs | 5 +---- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ListItemViewModel.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ListItemViewModel.cs index ab7990e781..48542ba628 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ListItemViewModel.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ListItemViewModel.cs @@ -107,17 +107,17 @@ public partial class ListItemViewModel(IListItem model, WeakReference + { + var vm = new TagViewModel(t, PageContext); + vm.InitializeProperties(); + return vm; + }) + .ToList() ?? []; + DoOnUiThread( () => { - var newTags = newTagsFromModel?.Select(t => - { - var vm = new TagViewModel(t, PageContext); - vm.InitializeProperties(); - return vm; - }) - .ToList() ?? []; - // Tags being an ObservableCollection instead of a List lead to // many COM exception issues. Tags = new(newTags); diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ShellViewModel.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ShellViewModel.cs index d86831d0a1..d9212733eb 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ShellViewModel.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ShellViewModel.cs @@ -4,7 +4,6 @@ using System.Runtime.InteropServices; using System.Runtime.Versioning; -using CommunityToolkit.Common; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Messaging; @@ -109,13 +108,11 @@ public partial class ShellViewModel(IServiceProvider _serviceProvider, TaskSched // TODO GH #239 switch back when using the new MD text block // _ = _queue.EnqueueAsync(() => _ = Task.Factory.StartNew( - async () => + () => { // bool f = await viewModel.InitializeCommand.ExecutionTask.; // var result = viewModel.InitializeCommand.ExecutionTask.GetResultOrDefault()!; // var result = viewModel.InitializeCommand.ExecutionTask.GetResultOrDefault()!; - var result = await viewModel.InitializeAsync(); - CurrentPage = viewModel; // result ? viewModel : null; ////LoadedState = result ? ViewModelLoadedState.Loaded : ViewModelLoadedState.Error; }, From 12e23e23a324ace2fa693fda97e6edea09314f31 Mon Sep 17 00:00:00 2001 From: Kai Tao <69313318+vanzue@users.noreply.github.com> Date: Mon, 19 May 2025 09:12:23 +0800 Subject: [PATCH 016/117] workspaces: shell:appsfolder launch does not support the command line (#39433) shell:appsfolder launch does not respect the command line --- src/modules/Workspaces/WorkspacesLauncher/AppLauncher.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/modules/Workspaces/WorkspacesLauncher/AppLauncher.cpp b/src/modules/Workspaces/WorkspacesLauncher/AppLauncher.cpp index 7075da2ea7..5cf36fc039 100644 --- a/src/modules/Workspaces/WorkspacesLauncher/AppLauncher.cpp +++ b/src/modules/Workspaces/WorkspacesLauncher/AppLauncher.cpp @@ -25,6 +25,7 @@ namespace AppLauncher const std::wstring ChromeFilename = L"chrome.exe"; const std::wstring ChromePwaFilename = L"chrome_proxy.exe"; const std::wstring PwaCommandLineAddition = L"--profile-directory=Default --app-id="; + const std::wstring SteamProtocolPrefix = L"steam:"; } Result LaunchApp(const std::wstring& appPath, const std::wstring& commandLineArgs, bool elevated) @@ -134,12 +135,11 @@ namespace AppLauncher } } - // win32 app with appUserModelId: - // usage example: steam games - if (!launched && !app.appUserModelId.empty()) + // protocol launch for steam + if (!launched && !app.appUserModelId.empty() && app.appUserModelId.contains(NonLocalizable::SteamProtocolPrefix)) { Logger::trace(L"Launching {} as {}", app.name, app.appUserModelId); - auto res = LaunchApp(L"shell:AppsFolder\\" + app.appUserModelId, app.commandLineArgs, app.isElevated); + auto res = LaunchApp(app.appUserModelId, app.commandLineArgs, app.isElevated); if (res.isOk()) { launched = true; From 5517c6d50446d839b48d179a9fdcb31d7c08b51b Mon Sep 17 00:00:00 2001 From: Hao Liu Date: Mon, 19 May 2025 13:00:40 +0800 Subject: [PATCH 017/117] Revert "[PowerAccent] Cancel previous ShowToolbar task if a new one is triggered" (#39563) Revert "[PowerAccent] Cancel previous ShowToolbar task if a new one is triggered (#37757)" This reverts commit e1ad7e39c69072ac21eeaec73f0b2de19dc93e4b. --- .../PowerAccent.Core/PowerAccent.cs | 18 +++++-- .../KeyboardListener.cpp | 47 ++++--------------- .../KeyboardListener.h | 11 +---- .../KeyboardListener.idl | 2 +- 4 files changed, 25 insertions(+), 53 deletions(-) diff --git a/src/modules/poweraccent/PowerAccent.Core/PowerAccent.cs b/src/modules/poweraccent/PowerAccent.Core/PowerAccent.cs index 5d32f1d565..c2f0698f25 100644 --- a/src/modules/poweraccent/PowerAccent.Core/PowerAccent.cs +++ b/src/modules/poweraccent/PowerAccent.Core/PowerAccent.cs @@ -62,11 +62,11 @@ public partial class PowerAccent : IDisposable private void SetEvents() { - _keyboardListener.SetShowToolbarEvent(new PowerToys.PowerAccentKeyboardService.ShowToolbar((LetterKey letterKey, TriggerKey trigger ) => + _keyboardListener.SetShowToolbarEvent(new PowerToys.PowerAccentKeyboardService.ShowToolbar((LetterKey letterKey) => { System.Windows.Application.Current.Dispatcher.Invoke(() => { - ShowToolbar(letterKey, trigger); + ShowToolbar(letterKey); }); })); @@ -92,15 +92,23 @@ public partial class PowerAccent : IDisposable })); } - private void ShowToolbar(LetterKey letterKey, TriggerKey trigger) + private void ShowToolbar(LetterKey letterKey) { _visible = true; _characters = GetCharacters(letterKey); _characterDescriptions = GetCharacterDescriptions(_characters); _showUnicodeDescription = _settingService.ShowUnicodeDescription; - OnChangeDisplay?.Invoke(true, _characters); - ProcessNextChar(trigger, false); + + Task.Delay(_settingService.InputTime).ContinueWith( + t => + { + if (_visible) + { + OnChangeDisplay?.Invoke(true, _characters); + } + }, + TaskScheduler.FromCurrentSynchronizationContext()); } private string[] GetCharacters(LetterKey letterKey) diff --git a/src/modules/poweraccent/PowerAccentKeyboardService/KeyboardListener.cpp b/src/modules/poweraccent/PowerAccentKeyboardService/KeyboardListener.cpp index 3d38d20b91..93fb5c0230 100644 --- a/src/modules/poweraccent/PowerAccentKeyboardService/KeyboardListener.cpp +++ b/src/modules/poweraccent/PowerAccentKeyboardService/KeyboardListener.cpp @@ -13,7 +13,7 @@ namespace winrt::PowerToys::PowerAccentKeyboardService::implementation { KeyboardListener::KeyboardListener() : - m_toolbarVisible(false), m_activationKeyHold(false), m_triggeredWithSpace(false), m_leftShiftPressed(false), m_rightShiftPressed(false), m_triggeredWithLeftArrow(false), m_triggeredWithRightArrow(false) + m_toolbarVisible(false), m_triggeredWithSpace(false), m_leftShiftPressed(false), m_rightShiftPressed(false), m_triggeredWithLeftArrow(false), m_triggeredWithRightArrow(false) { s_instance = this; LoggerHelpers::init_logger(L"PowerAccent", L"PowerAccentKeyboardService", "PowerAccent"); @@ -53,8 +53,8 @@ namespace winrt::PowerToys::PowerAccentKeyboardService::implementation void KeyboardListener::SetShowToolbarEvent(ShowToolbar showToolbarEvent) { - m_showToolbarCb = [trigger = std::move(showToolbarEvent)](LetterKey key, TriggerKey triggerKey) { - trigger(key, triggerKey); + m_showToolbarCb = [trigger = std::move(showToolbarEvent)](LetterKey key) { + trigger(key); }; } @@ -152,17 +152,6 @@ namespace winrt::PowerToys::PowerAccentKeyboardService::implementation return false; } - void KeyboardListener::BeginShowToolbar(std::chrono::milliseconds delay, LetterKey key, TriggerKey trigger) - { - std::unique_lock lock(toolbarMutex); - auto result = toolbarCV.wait_for(lock, delay); - if (result == std::cv_status::timeout) - { - m_toolbarVisible = true; - m_showToolbarCb(key, trigger); - } - } - bool KeyboardListener::OnKeyDown(KBDLLHOOKSTRUCT info) noexcept { auto letterKey = static_cast(info.vkCode); @@ -210,7 +199,7 @@ namespace winrt::PowerToys::PowerAccentKeyboardService::implementation } } - if (!m_toolbarVisible && !m_activationKeyHold && letterPressed != LetterKey::None && triggerPressed && !IsSuppressedByGameMode() && !IsForegroundAppExcluded()) + if (!m_toolbarVisible && letterPressed != LetterKey::None && triggerPressed && !IsSuppressedByGameMode() && !IsForegroundAppExcluded()) { Logger::debug(L"Show toolbar. Letter: {}, Trigger: {}", letterPressed, triggerPressed); @@ -218,21 +207,11 @@ namespace winrt::PowerToys::PowerAccentKeyboardService::implementation m_triggeredWithSpace = triggerPressed == VK_SPACE; m_triggeredWithLeftArrow = triggerPressed == VK_LEFT; m_triggeredWithRightArrow = triggerPressed == VK_RIGHT; - m_activationKeyHold = true; - m_bothKeysPressed = true; - if (toolbarThread != nullptr) - { - toolbarCV.notify_all(); - toolbarThread->join(); - } - toolbarThread = std::make_unique(std::bind(&KeyboardListener::BeginShowToolbar, this, m_settings.inputTime, letterPressed,static_cast(triggerPressed))); + m_toolbarVisible = true; + m_showToolbarCb(letterPressed); } - if (m_activationKeyHold && triggerPressed && !m_toolbarVisible) - { - return true; - } - else if (m_toolbarVisible && triggerPressed) + if (m_toolbarVisible && triggerPressed) { if (triggerPressed == VK_LEFT) { @@ -272,9 +251,8 @@ namespace winrt::PowerToys::PowerAccentKeyboardService::implementation { letterPressed = LetterKey::None; - if (m_toolbarVisible || m_bothKeysPressed) + if (m_toolbarVisible) { - m_bothKeysPressed = false; if (m_stopwatch.elapsed() < m_settings.inputTime) { Logger::debug(L"Activation too fast. Do nothing."); @@ -302,18 +280,11 @@ namespace winrt::PowerToys::PowerAccentKeyboardService::implementation Logger::debug(L"Hide toolbar event and input char"); m_hideToolbarCb(InputType::Char); + m_toolbarVisible = false; } } - auto triggerPressed = info.vkCode; - - if (m_activationKeyHold && (letterPressed == LetterKey::None || (triggerPressed == VK_SPACE || triggerPressed == VK_LEFT || triggerPressed == VK_RIGHT))) - { - m_activationKeyHold = false; - toolbarCV.notify_all(); - } - return false; } diff --git a/src/modules/poweraccent/PowerAccentKeyboardService/KeyboardListener.h b/src/modules/poweraccent/PowerAccentKeyboardService/KeyboardListener.h index 6ec1118775..61c28e1866 100644 --- a/src/modules/poweraccent/PowerAccentKeyboardService/KeyboardListener.h +++ b/src/modules/poweraccent/PowerAccentKeyboardService/KeyboardListener.h @@ -2,7 +2,6 @@ #include "KeyboardListener.g.h" #include -#include #include namespace winrt::PowerToys::PowerAccentKeyboardService::implementation @@ -45,7 +44,6 @@ namespace winrt::PowerToys::PowerAccentKeyboardService::implementation static LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam); private: - void BeginShowToolbar(std::chrono::milliseconds delay, LetterKey key, TriggerKey trigger); bool OnKeyDown(KBDLLHOOKSTRUCT info) noexcept; bool OnKeyUp(KBDLLHOOKSTRUCT info) noexcept; bool IsSuppressedByGameMode(); @@ -53,14 +51,9 @@ namespace winrt::PowerToys::PowerAccentKeyboardService::implementation static inline KeyboardListener* s_instance; HHOOK s_llKeyboardHook = nullptr; - std::atomic m_toolbarVisible; - bool m_activationKeyHold; - bool m_bothKeysPressed = false; - std::unique_ptr toolbarThread; - std::mutex toolbarMutex; - std::condition_variable toolbarCV; + bool m_toolbarVisible; PowerAccentSettings m_settings; - std::function m_showToolbarCb; + std::function m_showToolbarCb; std::function m_hideToolbarCb; std::function m_nextCharCb; std::function m_isLanguageLetterCb; diff --git a/src/modules/poweraccent/PowerAccentKeyboardService/KeyboardListener.idl b/src/modules/poweraccent/PowerAccentKeyboardService/KeyboardListener.idl index 9fc0f42e42..9bc8448c22 100644 --- a/src/modules/poweraccent/PowerAccentKeyboardService/KeyboardListener.idl +++ b/src/modules/poweraccent/PowerAccentKeyboardService/KeyboardListener.idl @@ -67,7 +67,7 @@ namespace PowerToys Char }; - [version(1.0), uuid(37197089-5438-4479-af57-30ab3f3c8be4)] delegate void ShowToolbar(LetterKey key, TriggerKey trigger); + [version(1.0), uuid(37197089-5438-4479-af57-30ab3f3c8be4)] delegate void ShowToolbar(LetterKey key); [version(1.0), uuid(8eb79d6b-1826-424f-9fbc-af21ae19725e)] delegate void HideToolbar(InputType inputType); [version(1.0), uuid(db72d45c-a5a2-446f-bdc1-506e9121764a)] delegate void NextChar(TriggerKey inputSpace, boolean shiftPressed); [version(1.0), uuid(20be2919-2b91-4313-b6e0-4c3484fe91ef)] delegate void IsLanguageLetter(LetterKey key, [out] boolean* result); From 9b3f8951f8fb4df306e13e0332d5c739a881f295 Mon Sep 17 00:00:00 2001 From: TheBestWebsite <164817940+TheBestWebsite@users.noreply.github.com> Date: Tue, 20 May 2025 16:49:07 -0700 Subject: [PATCH 018/117] Update README with 0.91.1 (#39597) --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 7dd5a35cd4..44c1a800c0 100644 --- a/README.md +++ b/README.md @@ -37,17 +37,17 @@ Go to the [Microsoft PowerToys GitHub releases page][github-release-link] and cl [github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.92%22 [github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.91%22 -[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.91.0/PowerToysUserSetup-0.91.0-x64.exe -[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.91.0/PowerToysUserSetup-0.91.0-arm64.exe -[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.91.0/PowerToysSetup-0.91.0-x64.exe -[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.91.0/PowerToysSetup-0.91.0-arm64.exe +[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.91.1/PowerToysUserSetup-0.91.1-x64.exe +[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.91.1/PowerToysUserSetup-0.91.1-arm64.exe +[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.91.1/PowerToysSetup-0.91.1-x64.exe +[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.91.1/PowerToysSetup-0.91.1-arm64.exe | Description | Filename | sha256 hash | |----------------|----------|-------------| -| Per user - x64 | [PowerToysUserSetup-0.91.0-x64.exe][ptUserX64] | 190DD702EDE2D3AC27A253DF8BC2416B1AF05E6594FF25CABEE844E6D3C8CCB0 | -| Per user - ARM64 | [PowerToysUserSetup-0.91.0-arm64.exe][ptUserArm64] | BE6C964C40147B5F7838E51A13837347756CC45E6AC5BC0DD11AF9AF605ABDCD | -| Machine wide - x64 | [PowerToysSetup-0.91.0-x64.exe][ptMachineX64] | 2308D896D9A66C56B98AC8B3CE9B7C945C7A2315551E36C118C7ECAC4A6D05C2 | -| Machine wide - ARM64 | [PowerToysSetup-0.91.0-arm64.exe][ptMachineArm64] | 28BD1FEFA22C52279C6B600E677B425B014D1F9190EA449D6C63FC2702092DA3 | +| Per user - x64 | [PowerToysUserSetup-0.91.1-x64.exe][ptUserX64] | 42EA4A3E8C79A5456476D19E72B3E2AB9393A89C4DC7442EB7EE5A1E3490D38A | +| Per user - ARM64 | [PowerToysUserSetup-0.91.1-arm64.exe][ptUserArm64] | F3F433FE04049F9197411D792AADEBF34E3BE7FE16327BD8B73D2A046ED8BAF6 | +| Machine wide - x64 | [PowerToysSetup-0.91.1-x64.exe][ptMachineX64] | EC4BC3A8625775866B0ED4577CCF83E6EC7B1A0AD267379DDBAF4FE49C7B5BDD | +| Machine wide - ARM64 | [PowerToysSetup-0.91.1-arm64.exe][ptMachineArm64] | 9CB8911008420D0E446AE3D5CE365E447FA4DF9DCF9337F3A80F933C00FC3689 | This is our preferred method. From bdf0b5ea230432ff923ef19e5c116726a472ebce Mon Sep 17 00:00:00 2001 From: Kai Tao <69313318+vanzue@users.noreply.github.com> Date: Wed, 21 May 2025 13:10:03 +0800 Subject: [PATCH 019/117] MWB: Fix firewall rule to allow remote connections from IPs outside the local subnet (#39595) * remove unnecessary restrict for local subnet only in firewal * remoteip set to any --- src/modules/MouseWithoutBorders/ModuleInterface/dllmain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/MouseWithoutBorders/ModuleInterface/dllmain.cpp b/src/modules/MouseWithoutBorders/ModuleInterface/dllmain.cpp index 67799aa6c5..6bb823453f 100644 --- a/src/modules/MouseWithoutBorders/ModuleInterface/dllmain.cpp +++ b/src/modules/MouseWithoutBorders/ModuleInterface/dllmain.cpp @@ -567,7 +567,7 @@ public: executable_args.append(L"\" & echo \"Adding an inbound firewall rule for PowerToys.MouseWithoutBorders.exe\""); executable_args.append(L" & netsh advfirewall firewall add rule name=\"PowerToys.MouseWithoutBorders\" dir=in action=allow program=\""); executable_args.append(executable_path); - executable_args.append(L"\" enable=yes remoteip=LocalSubnet profile=any protocol=tcp & pause\""); + executable_args.append(L"\" enable=yes remoteip=any profile=any protocol=tcp & pause\""); SHELLEXECUTEINFOW sei{ sizeof(sei) }; sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI }; From a804bf86a4290484630d50490190b86909ec63e6 Mon Sep 17 00:00:00 2001 From: Gordon Lam <73506701+yeelam-gordon@users.noreply.github.com> Date: Thu, 22 May 2025 20:19:24 -0700 Subject: [PATCH 020/117] Update to WinAppSDK 1.7.2 (#39592) Update to WinAppSDK 1.7.2 --- .pipelines/UpdateVersions.ps1 | 13 ++++++------- Directory.Packages.props | 2 +- NOTICE.md | 2 +- .../PowerToys.MeasureToolCore.vcxproj | 8 ++++---- .../MeasureTool/MeasureToolCore/packages.config | 2 +- .../Directory.Packages.props | 4 ++-- .../TemplateCmdPalExtension.csproj | 2 +- .../Microsoft.CmdPal.Common.csproj | 2 +- .../Microsoft.Terminal.UI.vcxproj | 4 ++-- .../Microsoft.CommandPalette.Extensions.vcxproj | 4 ++-- .../packages.config | 2 +- src/modules/peek/Peek.UI/Peek.UI.csproj | 2 +- .../PowerRenameUILib/PowerRenameUI.vcxproj | 8 ++++---- .../powerrename/PowerRenameUILib/packages.config | 2 +- 14 files changed, 28 insertions(+), 29 deletions(-) diff --git a/.pipelines/UpdateVersions.ps1 b/.pipelines/UpdateVersions.ps1 index a1bc5bef9a..0be3e3d30b 100644 --- a/.pipelines/UpdateVersions.ps1 +++ b/.pipelines/UpdateVersions.ps1 @@ -126,16 +126,15 @@ Get-ChildItem -Path $rootPath -Recurse packages.config | ForEach-Object { } # Update Directory.Packages.props file -$propsFile = [System.IO.Path]::Combine($rootPath,"Directory.Packages.props") -if (Test-Path $propsFile) { - $file = Read-FileWithEncoding -Path $propsFile +Get-ChildItem -Path $rootPath -Recurse "Directory.Packages.props" | ForEach-Object { + $file = Read-FileWithEncoding -Path $_.FullName $content = $file.Content if ($content -match '' $oldVersionString = '' $content = $content -replace $oldVersionString, $newVersionString - Write-FileWithEncoding -Path $propsFile -Content $content -Encoding $file.encoding - Write-Host "Modified " $propsFile + Write-FileWithEncoding -Path $_.FullName -Content $content -Encoding $file.encoding + Write-Host "Modified " $_.FullName } } @@ -144,8 +143,8 @@ Get-ChildItem -Path $rootPath -Recurse *.vcxproj | ForEach-Object { $file = Read-FileWithEncoding -Path $_.FullName $content = $file.Content if ($content -match '\\Microsoft.WindowsAppSDK.') { - $newVersionString = '\Microsoft.WindowsAppSDK.' + $WinAppSDKVersion + '\' - $oldVersionString = '\\Microsoft.WindowsAppSDK.[-.0-9a-zA-Z]*\\' + $newVersionString = '\Microsoft.WindowsAppSDK.' + $WinAppSDKVersion + $oldVersionString = '\\Microsoft.WindowsAppSDK.(?=[-.0-9a-zA-Z]*\d)[-.0-9a-zA-Z]*' #positive lookahead for at least a digit $content = $content -replace $oldVersionString, $newVersionString Write-FileWithEncoding -Path $_.FullName -Content $content -Encoding $file.encoding Write-Host "Modified " $_.FullName diff --git a/Directory.Packages.props b/Directory.Packages.props index 083a66e5a9..a4ac308b23 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -55,7 +55,7 @@ --> - + diff --git a/NOTICE.md b/NOTICE.md index 21d51192c4..9fb3dda87d 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -1472,7 +1472,7 @@ SOFTWARE. - Microsoft.Windows.CsWin32 0.2.46-beta - Microsoft.Windows.CsWinRT 2.2.0 - Microsoft.Windows.SDK.BuildTools 10.0.22621.2428 -- Microsoft.WindowsAppSDK 1.7.250401001 +- Microsoft.WindowsAppSDK 1.7.250513003 - Microsoft.WindowsPackageManager.ComInterop 1.10.340 - Microsoft.Xaml.Behaviors.WinUI.Managed 2.0.9 - Microsoft.Xaml.Behaviors.Wpf 1.1.39 diff --git a/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj b/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj index f0b12187ad..41a968dbae 100644 --- a/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj +++ b/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj @@ -1,6 +1,6 @@  - + @@ -141,7 +141,7 @@ - + @@ -153,7 +153,7 @@ - - + + \ No newline at end of file diff --git a/src/modules/MeasureTool/MeasureToolCore/packages.config b/src/modules/MeasureTool/MeasureToolCore/packages.config index 996736442d..f6fdf954c9 100644 --- a/src/modules/MeasureTool/MeasureToolCore/packages.config +++ b/src/modules/MeasureTool/MeasureToolCore/packages.config @@ -4,5 +4,5 @@ - + \ No newline at end of file diff --git a/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/Directory.Packages.props b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/Directory.Packages.props index 7a8dfdc97a..cf286b3822 100644 --- a/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/Directory.Packages.props +++ b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/Directory.Packages.props @@ -1,4 +1,4 @@ - + true @@ -9,7 +9,7 @@ - + diff --git a/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/TemplateCmdPalExtension.csproj b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/TemplateCmdPalExtension.csproj index 72e6d7400b..af4ca3525d 100644 --- a/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/TemplateCmdPalExtension.csproj +++ b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/TemplateCmdPalExtension.csproj @@ -1,4 +1,4 @@ - + WinExe TemplateCmdPalExtension diff --git a/src/modules/cmdpal/Microsoft.CmdPal.Common/Microsoft.CmdPal.Common.csproj b/src/modules/cmdpal/Microsoft.CmdPal.Common/Microsoft.CmdPal.Common.csproj index 5f83ca54e1..0112da1b0b 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.Common/Microsoft.CmdPal.Common.csproj +++ b/src/modules/cmdpal/Microsoft.CmdPal.Common/Microsoft.CmdPal.Common.csproj @@ -1,4 +1,4 @@ - + diff --git a/src/modules/cmdpal/Microsoft.Terminal.UI/Microsoft.Terminal.UI.vcxproj b/src/modules/cmdpal/Microsoft.Terminal.UI/Microsoft.Terminal.UI.vcxproj index a3c282b7f1..43c890dd31 100644 --- a/src/modules/cmdpal/Microsoft.Terminal.UI/Microsoft.Terminal.UI.vcxproj +++ b/src/modules/cmdpal/Microsoft.Terminal.UI/Microsoft.Terminal.UI.vcxproj @@ -1,9 +1,9 @@ - + ..\..\..\..\ - $(PathToRoot)packages\Microsoft.WindowsAppSDK.1.7.250401001 + $(PathToRoot)packages\Microsoft.WindowsAppSDK.1.7.250513003 diff --git a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/Microsoft.CommandPalette.Extensions.vcxproj b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/Microsoft.CommandPalette.Extensions.vcxproj index e7d8875326..5383686a55 100644 --- a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/Microsoft.CommandPalette.Extensions.vcxproj +++ b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/Microsoft.CommandPalette.Extensions.vcxproj @@ -1,8 +1,8 @@ - + ..\..\..\..\..\ - $(PathToRoot)packages\Microsoft.WindowsAppSDK.1.7.250401001 + $(PathToRoot)packages\Microsoft.WindowsAppSDK.1.7.250513003 $(PathToRoot)packages\Microsoft.Windows.CppWinRT.2.0.240111.5 $(PathToRoot)packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428 $(PathToRoot)packages\Microsoft.Web.WebView2.1.0.2903.40 diff --git a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/packages.config b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/packages.config index 608661db25..6a27f0cdda 100644 --- a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/packages.config +++ b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/packages.config @@ -3,5 +3,5 @@ - + \ No newline at end of file diff --git a/src/modules/peek/Peek.UI/Peek.UI.csproj b/src/modules/peek/Peek.UI/Peek.UI.csproj index 673ff5c9e7..070d9d2ef2 100644 --- a/src/modules/peek/Peek.UI/Peek.UI.csproj +++ b/src/modules/peek/Peek.UI/Peek.UI.csproj @@ -1,4 +1,4 @@ - + diff --git a/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj b/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj index 806da3904b..e124c83726 100644 --- a/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj +++ b/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj @@ -1,6 +1,6 @@  - + @@ -207,7 +207,7 @@ - + @@ -221,8 +221,8 @@ - - + + diff --git a/src/modules/powerrename/PowerRenameUILib/packages.config b/src/modules/powerrename/PowerRenameUILib/packages.config index 77b75fad7e..268846b6a0 100644 --- a/src/modules/powerrename/PowerRenameUILib/packages.config +++ b/src/modules/powerrename/PowerRenameUILib/packages.config @@ -6,5 +6,5 @@ - + \ No newline at end of file From 718e72557182df210a476964c4f13cbb377b47f6 Mon Sep 17 00:00:00 2001 From: Yu Leng <42196638+moooyo@users.noreply.github.com> Date: Mon, 26 May 2025 11:37:38 +0800 Subject: [PATCH 021/117] [cmdpal] Fix calculator error when pressing Space (#39679) Co-authored-by: Yu Leng (from Dev Box) --- .../Microsoft.CmdPal.Ext.Calc/Helper/CalculateHelper.cs | 2 +- .../ext/Microsoft.CmdPal.Ext.Calc/Helper/QueryHelper.cs | 5 +++++ .../Properties/Resources.Designer.cs | 9 +++++++++ .../Microsoft.CmdPal.Ext.Calc/Properties/Resources.resx | 3 +++ 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/CalculateHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/CalculateHelper.cs index acab3d7b96..deb441cfe2 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/CalculateHelper.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/CalculateHelper.cs @@ -35,7 +35,7 @@ public static class CalculateHelper { if (string.IsNullOrWhiteSpace(input)) { - throw new ArgumentNullException(paramName: nameof(input)); + return false; } if (!RegValidExpressChar.IsMatch(input)) diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/QueryHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/QueryHelper.cs index 6b1e62ae03..5805a6a0ad 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/QueryHelper.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/QueryHelper.cs @@ -32,6 +32,11 @@ public static partial class QueryHelper NumberTranslator translator = NumberTranslator.Create(inputCulture, new CultureInfo("en-US")); var input = translator.Translate(query.Normalize(NormalizationForm.FormKC)); + if (string.IsNullOrWhiteSpace(input)) + { + return ErrorHandler.OnError(isFallbackSearch, query, Properties.Resources.calculator_expression_empty); + } + if (!CalculateHelper.InputValid(input)) { return null; diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Properties/Resources.Designer.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Properties/Resources.Designer.cs index 8cd385be32..8759c1209f 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Properties/Resources.Designer.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Properties/Resources.Designer.cs @@ -132,6 +132,15 @@ namespace Microsoft.CmdPal.Ext.Calc.Properties { } } + /// + /// Looks up a localized string similar to Please enter an expression. + /// + public static string calculator_expression_empty { + get { + return ResourceManager.GetString("calculator_expression_empty", resourceCulture); + } + } + /// /// Looks up a localized string similar to Expression wrong or incomplete. /// diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Properties/Resources.resx b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Properties/Resources.resx index 3c50d3a1c5..dba2ba2067 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Properties/Resources.resx +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Properties/Resources.resx @@ -199,4 +199,7 @@ Copy binary + + Please enter an expression + \ No newline at end of file From 53b989857be05c829543a0f7ffe76017223960d2 Mon Sep 17 00:00:00 2001 From: Bradley Myers Date: Mon, 26 May 2025 05:03:35 -0400 Subject: [PATCH 022/117] Option to toggle the system tray icon (#23220) * Added option to toggle the system tray icon At the moment, this hides the icon making the settings window inaccessible without first modifying the general `settings.json` file. * Use IPC messages to manage the tray icon settings * Fix launching second window binds to active settings process * Added context menu option to hide tray icon * Added Exit PT button to settings ui NavigationView.PaneFooter * Moved DllImports to NativeMethods.cs * Sentence case titles * Fix whitespace * Re-add exit icon to NavView * Re-added toggle switch to new UI * Fix build * Fix build after merge main * Fix the string to display * add shut down buttons * finish polish * fix string * Styling tweaks to titlebar and settingscards * fix comment * fix unit test * fix ut --------- Co-authored-by: Jaime Bernardo Co-authored-by: vanzue Co-authored-by: Kayla Cinnamon Co-authored-by: Niels Laute --- src/runner/Resources.resx | 66 +++++++++++++++++- src/runner/general_settings.cpp | 14 +++- src/runner/general_settings.h | 1 + src/runner/main.cpp | 2 + src/runner/resource.base.h | 3 +- src/runner/runner.base.rc | Bin 3146 -> 3092 bytes src/runner/tray_icon.cpp | 18 +++++ src/runner/tray_icon.h | 3 + .../Settings.UI.Library/GeneralSettings.cs | 5 ++ .../ViewModelTests/General.cs | 30 ++++++++ .../Settings.UI/Helpers/NativeMethods.cs | 7 ++ .../Settings.UI/SettingsXAML/App.xaml | 1 + .../Controls/SettingsGroup/SettingsGroup.xaml | 2 +- .../SettingsXAML/Flyout/LaunchPage.xaml.cs | 2 +- .../SettingsXAML/Views/GeneralPage.xaml | 16 ++++- .../SettingsXAML/Views/GeneralPage.xaml.cs | 20 ++++++ .../SettingsXAML/Views/ShellPage.xaml | 27 ++++++- .../SettingsXAML/Views/ShellPage.xaml.cs | 15 ++++ .../Settings.UI/Strings/en-us/Resources.resw | 27 +++++++ .../ViewModels/GeneralViewModel.cs | 21 ++++++ 20 files changed, 270 insertions(+), 10 deletions(-) diff --git a/src/runner/Resources.resx b/src/runner/Resources.resx index 902e3a0874..2986ec6cef 100644 --- a/src/runner/Resources.resx +++ b/src/runner/Resources.resx @@ -1,5 +1,64 @@  + @@ -89,7 +148,7 @@ This setting has been disabled by your administrator. - This setting has been disabled manually via <a href="https://ms_settings_startupapps" target="_blank">Startup Settings</a>. + This setting has been disabled manually via <a href="https://ms_settings_startupapps" target="_blank">Startup Settings</a>. An update to PowerToys is available. @@ -121,6 +180,9 @@ Exit Exit as a verb, as in Exit the application + + Show icon + Report bug @@ -134,4 +196,4 @@ Administrator - + \ No newline at end of file diff --git a/src/runner/general_settings.cpp b/src/runner/general_settings.cpp index 83128b05e9..bf80fb2df8 100644 --- a/src/runner/general_settings.cpp +++ b/src/runner/general_settings.cpp @@ -1,6 +1,7 @@ #include "pch.h" #include "general_settings.h" #include "auto_start_helper.h" +#include "tray_icon.h" #include "Generated files/resource.h" #include @@ -14,6 +15,7 @@ // TODO: would be nice to get rid of these globals, since they're basically cached json settings static std::wstring settings_theme = L"system"; +static bool show_tray_icon = true; static bool run_as_elevated = false; static bool show_new_updates_toast_notification = true; static bool download_updates_automatically = true; @@ -38,6 +40,7 @@ json::JsonObject GeneralSettings::to_json() } result.SetNamedValue(L"enabled", std::move(enabled)); + result.SetNamedValue(L"show_tray_icon", json::value(showSystemTrayIcon)); result.SetNamedValue(L"is_elevated", json::value(isElevated)); result.SetNamedValue(L"run_elevated", json::value(isRunElevated)); result.SetNamedValue(L"show_new_updates_toast_notification", json::value(showNewUpdatesToastNotification)); @@ -74,7 +77,9 @@ json::JsonObject load_general_settings() GeneralSettings get_general_settings() { const bool is_user_admin = check_user_is_admin(); - GeneralSettings settings{ + GeneralSettings settings + { + .showSystemTrayIcon = show_tray_icon, .isElevated = is_process_elevated(), .isRunElevated = run_as_elevated, .isAdmin = is_user_admin, @@ -214,6 +219,13 @@ void apply_general_settings(const json::JsonObject& general_configs, bool save) settings_theme = general_configs.GetNamedString(L"theme"); } + if (json::has(general_configs, L"show_tray_icon", json::JsonValueType::Boolean)) + { + show_tray_icon = general_configs.GetNamedBoolean(L"show_tray_icon"); + // Update tray icon visibility when setting is toggled + set_tray_icon_visible(show_tray_icon); + } + if (save) { GeneralSettings save_settings = get_general_settings(); diff --git a/src/runner/general_settings.h b/src/runner/general_settings.h index f56271cf07..ef2224b132 100644 --- a/src/runner/general_settings.h +++ b/src/runner/general_settings.h @@ -5,6 +5,7 @@ struct GeneralSettings { bool isStartupEnabled; + bool showSystemTrayIcon; std::wstring startupDisabledReason; std::map isModulesEnabledMap; bool isElevated; diff --git a/src/runner/main.cpp b/src/runner/main.cpp index e9ea4e59d9..91f9639c05 100644 --- a/src/runner/main.cpp +++ b/src/runner/main.cpp @@ -90,6 +90,7 @@ void open_menu_from_another_instance(std::optional settings_window) msg = static_cast(ESettingsWindowNames_from_string(settings_window.value())); } PostMessageW(hwnd_main, WM_COMMAND, ID_SETTINGS_MENU_COMMAND, msg); + SetForegroundWindow(hwnd_main); // Bring the settings window to the front } int runner(bool isProcessElevated, bool openSettings, std::string settingsWindow, bool openOobe, bool openScoobe, bool showRestartNotificationAfterUpdate) @@ -104,6 +105,7 @@ int runner(bool isProcessElevated, bool openSettings, std::string settingsWindow #endif Trace::RegisterProvider(); start_tray_icon(isProcessElevated); + set_tray_icon_visible(get_general_settings().showSystemTrayIcon); CentralizedKeyboardHook::Start(); int result = -1; diff --git a/src/runner/resource.base.h b/src/runner/resource.base.h index 22d040cc82..0ee4cbb2e5 100644 --- a/src/runner/resource.base.h +++ b/src/runner/resource.base.h @@ -20,4 +20,5 @@ #define ID_ABOUT_MENU_COMMAND 40003 #define ID_REPORT_BUG_COMMAND 40004 #define ID_DOCUMENTATION_MENU_COMMAND 40005 -#define ID_QUICK_ACCESS_MENU_COMMAND 40006 \ No newline at end of file +#define ID_QUICK_ACCESS_MENU_COMMAND 40006 +#define ID_SHOW_TRAY_ICON_MENU_COMMAND 40007 diff --git a/src/runner/runner.base.rc b/src/runner/runner.base.rc index 5737649c37fdeb87cd10216387ba8ae325ad55ac..9f56295553d8fd29c64c47c2b508fcf01b29a887 100644 GIT binary patch delta 83 zcmX>lF-2lS3*%%dCV|avOma+?!3-G;`3&U@3JjhM$v{>fgA#)dnCHR}&k)Su!Qjsj f4rGNe1Ti==L;_i!49-9?zsZSA`kMn-ez5}p;tUZG delta 135 zcmbOtaY|xC3u9m)Ln%WhLo!1)g91Y$kWOYuWhe&17={uCA0RsoNb3ScbD*k}7<7Ot zJsDgW;u!)NLV+x225%tS5lA~TxH1G!Udw1Wxqwkc8f1bCLq5>_B%tn8WK%XzW|U{z JtjF?<9RO^D925Wm diff --git a/src/runner/tray_icon.cpp b/src/runner/tray_icon.cpp index 015fb158b8..30cca89951 100644 --- a/src/runner/tray_icon.cpp +++ b/src/runner/tray_icon.cpp @@ -2,6 +2,7 @@ #include "Generated files/resource.h" #include "settings_window.h" #include "tray_icon.h" +#include "general_settings.h" #include "centralized_hotkeys.h" #include "centralized_kb_hook.h" #include @@ -90,6 +91,13 @@ void handle_tray_command(HWND window, const WPARAM command_id, LPARAM lparam) } DestroyWindow(window); break; + case ID_SHOW_TRAY_ICON_MENU_COMMAND: + { + GeneralSettings settings = get_general_settings(); + settings.showSystemTrayIcon = true; + apply_general_settings(settings.to_json(), true); + break; + } case ID_ABOUT_MENU_COMMAND: if (!about_box_shown) { @@ -191,11 +199,13 @@ LRESULT __stdcall tray_icon_window_proc(HWND window, UINT message, WPARAM wparam { static std::wstring settings_menuitem_label = GET_RESOURCE_STRING(IDS_SETTINGS_MENU_TEXT); static std::wstring exit_menuitem_label = GET_RESOURCE_STRING(IDS_EXIT_MENU_TEXT); + static std::wstring show_tray_icon_menuitem_label = GET_RESOURCE_STRING(IDS_SHOW_TRAY_ICON_MENU_TEXT); static std::wstring submit_bug_menuitem_label = GET_RESOURCE_STRING(IDS_SUBMIT_BUG_TEXT); static std::wstring documentation_menuitem_label = GET_RESOURCE_STRING(IDS_DOCUMENTATION_MENU_TEXT); static std::wstring quick_access_menuitem_label = GET_RESOURCE_STRING(IDS_QUICK_ACCESS_MENU_TEXT); change_menu_item_text(ID_SETTINGS_MENU_COMMAND, settings_menuitem_label.data()); change_menu_item_text(ID_EXIT_MENU_COMMAND, exit_menuitem_label.data()); + change_menu_item_text(ID_SHOW_TRAY_ICON_MENU_COMMAND, show_tray_icon_menuitem_label.data()); change_menu_item_text(ID_REPORT_BUG_COMMAND, submit_bug_menuitem_label.data()); change_menu_item_text(ID_DOCUMENTATION_MENU_COMMAND, documentation_menuitem_label.data()); change_menu_item_text(ID_QUICK_ACCESS_MENU_COMMAND, quick_access_menuitem_label.data()); @@ -312,6 +322,14 @@ void start_tray_icon(bool isProcessElevated) } } +void set_tray_icon_visible(bool shouldIconBeVisible) +{ + tray_icon_data.uFlags |= NIF_STATE; + tray_icon_data.dwStateMask = NIS_HIDDEN; + tray_icon_data.dwState = shouldIconBeVisible ? 0 : NIS_HIDDEN; + Shell_NotifyIcon(NIM_MODIFY, &tray_icon_data); +} + void stop_tray_icon() { if (tray_icon_created) diff --git a/src/runner/tray_icon.h b/src/runner/tray_icon.h index 3c323053ca..4fa7ebfe5a 100644 --- a/src/runner/tray_icon.h +++ b/src/runner/tray_icon.h @@ -4,6 +4,8 @@ // Start the Tray Icon void start_tray_icon(bool isProcessElevated); +// Change the Tray Icon visibility +void set_tray_icon_visible(bool shouldIconBeVisible); // Stop the Tray Icon void stop_tray_icon(); // Open the Settings Window @@ -13,4 +15,5 @@ typedef void (*main_loop_callback_function)(PVOID); // Calls a callback in _callback bool dispatch_run_on_main_ui_thread(main_loop_callback_function _callback, PVOID data); +// Must be the same as: settings-ui/Settings.UI/Views/ShellPage.xaml.cs -> ExitPTItem_Tapped() -> const string ptTrayIconWindowClass const inline wchar_t* pt_tray_icon_window_class = L"PToyTrayIconWindow"; \ No newline at end of file diff --git a/src/settings-ui/Settings.UI.Library/GeneralSettings.cs b/src/settings-ui/Settings.UI.Library/GeneralSettings.cs index ed7b503150..3d295284e3 100644 --- a/src/settings-ui/Settings.UI.Library/GeneralSettings.cs +++ b/src/settings-ui/Settings.UI.Library/GeneralSettings.cs @@ -19,6 +19,10 @@ namespace Microsoft.PowerToys.Settings.UI.Library [JsonPropertyName("startup")] public bool Startup { get; set; } + // Gets or sets a value indicating whether the powertoys system tray icon should be hidden. + [JsonPropertyName("show_tray_icon")] + public bool ShowSysTrayIcon { get; set; } + // Gets or sets a value indicating whether the powertoy elevated. [CmdConfigureIgnoreAttribute] [JsonPropertyName("is_elevated")] @@ -75,6 +79,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library public GeneralSettings() { Startup = false; + ShowSysTrayIcon = true; IsAdmin = false; EnableWarningsElevatedApps = true; IsElevated = false; diff --git a/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/General.cs b/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/General.cs index 70bfbe4fba..8b47e8147a 100644 --- a/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/General.cs +++ b/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/General.cs @@ -224,6 +224,36 @@ namespace ViewModelTests viewModel.ThemeIndex = 0; } + [TestMethod] + public void IsShowSysTrayIconEnabledByDefaultShouldDisableWhenSuccessful() + { + // Arrange + // Assert + Func sendMockIPCConfigMSG = msg => + { + OutGoingGeneralSettings snd = JsonSerializer.Deserialize(msg); + Assert.IsFalse(snd.GeneralSettings.ShowSysTrayIcon); + return 0; + }; + + Func sendRestartAdminIPCMessage = msg => { return 0; }; + Func sendCheckForUpdatesIPCMessage = msg => { return 0; }; + GeneralViewModel viewModel = new( + settingsRepository: SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), + "GeneralSettings_RunningAsAdminText", + "GeneralSettings_RunningAsUserText", + false, + false, + sendMockIPCConfigMSG, + sendRestartAdminIPCMessage, + sendCheckForUpdatesIPCMessage, + GeneralSettingsFileName); + Assert.IsTrue(viewModel.ShowSysTrayIcon); + + // Act + viewModel.ShowSysTrayIcon = false; + } + [TestMethod] public void AllModulesAreEnabledByDefault() { diff --git a/src/settings-ui/Settings.UI/Helpers/NativeMethods.cs b/src/settings-ui/Settings.UI/Helpers/NativeMethods.cs index 072f12b00c..35132b2049 100644 --- a/src/settings-ui/Settings.UI/Helpers/NativeMethods.cs +++ b/src/settings-ui/Settings.UI/Helpers/NativeMethods.cs @@ -17,6 +17,7 @@ namespace Microsoft.PowerToys.Settings.UI.Helpers internal const int SW_SHOWNORMAL = 1; internal const int SW_SHOWMAXIMIZED = 3; internal const int SW_HIDE = 0; + internal const int WM_COMMAND = 0x0111; // https://learn.microsoft.com/en-us/windows/win32/menurc/wm-command [DllImport("user32.dll")] internal static extern IntPtr GetActiveWindow(); @@ -48,6 +49,12 @@ namespace Microsoft.PowerToys.Settings.UI.Helpers [DllImport("Comdlg32.dll", CharSet = CharSet.Unicode)] internal static extern bool GetOpenFileName([In, Out] OpenFileName openFileName); + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + internal static extern IntPtr FindWindow(string lpClassName, string lpWindowName); + + [DllImport("user32.dll")] + internal static extern IntPtr SendMessage(IntPtr hWnd, IntPtr msg, UIntPtr wParam, UIntPtr lParam); + [DllImport("comdlg32.dll", CharSet = CharSet.Auto, EntryPoint = "ChooseFont", SetLastError = true)] internal static extern bool ChooseFont(IntPtr lpChooseFont); diff --git a/src/settings-ui/Settings.UI/SettingsXAML/App.xaml b/src/settings-ui/Settings.UI/SettingsXAML/App.xaml index 365c592d27..c1a5392bdc 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/App.xaml +++ b/src/settings-ui/Settings.UI/SettingsXAML/App.xaml @@ -39,6 +39,7 @@ + 2 6,16,16,16 diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Controls/SettingsGroup/SettingsGroup.xaml b/src/settings-ui/Settings.UI/SettingsXAML/Controls/SettingsGroup/SettingsGroup.xaml index d3462c6655..912211d9c4 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Controls/SettingsGroup/SettingsGroup.xaml +++ b/src/settings-ui/Settings.UI/SettingsXAML/Controls/SettingsGroup/SettingsGroup.xaml @@ -10,7 +10,7 @@ + Spacing="{StaticResource SettingsCardSpacing}" /> diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Flyout/LaunchPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Flyout/LaunchPage.xaml.cs index dadcd78c68..aad7dcf215 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Flyout/LaunchPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Flyout/LaunchPage.xaml.cs @@ -176,7 +176,7 @@ namespace Microsoft.PowerToys.Settings.UI.Flyout }); } - private void ReportBugBtn_Click(object sender, RoutedEventArgs e) + internal void ReportBugBtn_Click(object sender, RoutedEventArgs e) { ViewModel.StartBugReport(); diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/GeneralPage.xaml b/src/settings-ui/Settings.UI/SettingsXAML/Views/GeneralPage.xaml index 079a634e97..13d6ee5c8f 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/GeneralPage.xaml +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/GeneralPage.xaml @@ -266,6 +266,10 @@ + + + + - + - + + + Show the release notes after an update + + This settings page is accessible by running the PowerToys executable again + + + Show system tray icon + Do not activate when Game Mode is on "Game mode" is the Windows feature to prevent notification when playing a game. @@ -5020,4 +5026,25 @@ To record a specific window, enter the hotkey with the Alt key in the opposite m hue (Oklch) + + Exit PowerToys + + + System + + + Generate bug report package + + + Create zip folder with logs on the Desktop + + + Generate package + + + Shut down + + + Bug report package is being created + \ No newline at end of file diff --git a/src/settings-ui/Settings.UI/ViewModels/GeneralViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/GeneralViewModel.cs index c535ffa579..6b3bb8243a 100644 --- a/src/settings-ui/Settings.UI/ViewModels/GeneralViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/GeneralViewModel.cs @@ -146,6 +146,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels _startup = GeneralSettingsConfig.Startup; } + _showSysTrayIcon = GeneralSettingsConfig.ShowSysTrayIcon; _showNewUpdatesToastNotification = GeneralSettingsConfig.ShowNewUpdatesToastNotification; _autoDownloadUpdates = GeneralSettingsConfig.AutoDownloadUpdates; _showWhatsNewAfterUpdates = GeneralSettingsConfig.ShowWhatsNewAfterUpdates; @@ -228,6 +229,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels private static bool _isDevBuild; private bool _startup; + private bool _showSysTrayIcon; private GpoRuleConfigured _runAtStartupGpoRuleConfiguration; private bool _runAtStartupIsGPOConfigured; private bool _isElevated; @@ -359,6 +361,25 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels } } + // Gets or sets a value indicating whether the PowerToys icon should be shown in the system tray. + public bool ShowSysTrayIcon + { + get + { + return _showSysTrayIcon; + } + + set + { + if (_showSysTrayIcon != value) + { + _showSysTrayIcon = value; + GeneralSettingsConfig.ShowSysTrayIcon = value; + NotifyPropertyChanged(); + } + } + } + public string RunningAsText { get From f642720087668ac9faf32545bf23c5f314d16134 Mon Sep 17 00:00:00 2001 From: PesBandi <127593627+PesBandi@users.noreply.github.com> Date: Wed, 28 May 2025 08:25:07 +0200 Subject: [PATCH 023/117] [CmdPal][Calc]Trim leading `=` to support pasting formulas (#39743) * [CmdPal][Calc]Trim leading `=` to support pasting formulas * Update README.md * Update README.md --- .../cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/QueryHelper.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/QueryHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/QueryHelper.cs index 5805a6a0ad..7cbdf4035f 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/QueryHelper.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/QueryHelper.cs @@ -23,6 +23,9 @@ public static partial class QueryHelper CultureInfo inputCulture = settings.InputUseEnglishFormat ? new CultureInfo("en-us") : CultureInfo.CurrentCulture; CultureInfo outputCulture = settings.OutputUseEnglishFormat ? new CultureInfo("en-us") : CultureInfo.CurrentCulture; + // In case the user pastes a query with a leading = + query = query.TrimStart('='); + // Happens if the user has only typed the action key so far if (string.IsNullOrEmpty(query)) { From 02a92694353bfe985ec4b839c639071d5f5c00ed Mon Sep 17 00:00:00 2001 From: Mengyuan <162882040+chenmy77@users.noreply.github.com> Date: Wed, 28 May 2025 14:56:37 +0800 Subject: [PATCH 024/117] [Fuzz] Add Fuzz testing for FancyZones (#38165) * Add FancyZones.FuzzTests Module for fuzzing grid from json element * remove unuse test cs * fix spell error * remove specific modify in job-fuzz.yml * add Testably.Abstractions.FileSystem.Interface.dll * fix spell error * add dependency dll * add annotations * fix pipeline error need to import 'Common.Dotnet.CsWinRT.props' --- PowerToys.sln | 11 +++ .../FancyZones.FuzzTests.csproj | 36 ++++++++ .../FancyZones.FuzzTests/FuzzTests.cs | 86 +++++++++++++++++++ .../FancyZones.FuzzTests/MSTestSettings.cs | 5 ++ .../FancyZones.FuzzTests/OneFuzzConfig.json | 51 +++++++++++ 5 files changed, 189 insertions(+) create mode 100644 src/modules/fancyzones/FancyZones.FuzzTests/FancyZones.FuzzTests.csproj create mode 100644 src/modules/fancyzones/FancyZones.FuzzTests/FuzzTests.cs create mode 100644 src/modules/fancyzones/FancyZones.FuzzTests/MSTestSettings.cs create mode 100644 src/modules/fancyzones/FancyZones.FuzzTests/OneFuzzConfig.json diff --git a/PowerToys.sln b/PowerToys.sln index 201c0fbefe..1db5745527 100644 --- a/PowerToys.sln +++ b/PowerToys.sln @@ -706,6 +706,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RegistryPreview.FuzzTests", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Ext.System", "src\modules\cmdpal\ext\Microsoft.CmdPal.Ext.System\Microsoft.CmdPal.Ext.System.csproj", "{64B88F02-CD88-4ED8-9624-989A800230F9}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FancyZones.FuzzTests", "src\modules\fancyzones\FancyZones.FuzzTests\FancyZones.FuzzTests.csproj", "{0217E86E-3476-9946-DE8E-9D200CEBD47A}" +EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CmdPalKeyboardService", "src\modules\cmdpal\CmdPalKeyboardService\CmdPalKeyboardService.vcxproj", "{5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerRename.FuzzingTest", "src\modules\powerrename\PowerRename.FuzzingTest\PowerRename.FuzzingTest.vcxproj", "{2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}" @@ -2596,6 +2598,14 @@ Global {2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}.Release|ARM64.ActiveCfg = Release|ARM64 {2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}.Release|x64.ActiveCfg = Release|x64 {2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}.Release|x64.Build.0 = Release|x64 + {0217E86E-3476-9946-DE8E-9D200CEBD47A}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {0217E86E-3476-9946-DE8E-9D200CEBD47A}.Debug|ARM64.Build.0 = Debug|ARM64 + {0217E86E-3476-9946-DE8E-9D200CEBD47A}.Debug|x64.ActiveCfg = Debug|x64 + {0217E86E-3476-9946-DE8E-9D200CEBD47A}.Debug|x64.Build.0 = Debug|x64 + {0217E86E-3476-9946-DE8E-9D200CEBD47A}.Release|ARM64.ActiveCfg = Release|ARM64 + {0217E86E-3476-9946-DE8E-9D200CEBD47A}.Release|ARM64.Build.0 = Release|ARM64 + {0217E86E-3476-9946-DE8E-9D200CEBD47A}.Release|x64.ActiveCfg = Release|x64 + {0217E86E-3476-9946-DE8E-9D200CEBD47A}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2866,6 +2876,7 @@ Global {4E0AE3A4-2EE0-44D7-A2D0-8769977254A0} = {F05E590D-AD46-42BE-9C25-6A63ADD2E3EA} {5702B3CC-8575-48D5-83D8-15BB42269CD3} = {929C1324-22E8-4412-A9A8-80E85F3985A5} {64B88F02-CD88-4ED8-9624-989A800230F9} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2} + {0217E86E-3476-9946-DE8E-9D200CEBD47A} = {D1D6BC88-09AE-4FB4-AD24-5DED46A791DD} {5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2} = {3846508C-77EB-4034-A702-F8BB263C4F79} {2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E} = {89E20BCE-EB9C-46C8-8B50-E01A82E6FDC3} EndGlobalSection diff --git a/src/modules/fancyzones/FancyZones.FuzzTests/FancyZones.FuzzTests.csproj b/src/modules/fancyzones/FancyZones.FuzzTests/FancyZones.FuzzTests.csproj new file mode 100644 index 0000000000..9890b67217 --- /dev/null +++ b/src/modules/fancyzones/FancyZones.FuzzTests/FancyZones.FuzzTests.csproj @@ -0,0 +1,36 @@ + + + + + + + + ..\..\..\..\$(Platform)\$(Configuration)\tests\FancyZones.FuzzTests\ + + + + + + + + + + + + + + + + + + + + + + + + PreserveNewest + + + + diff --git a/src/modules/fancyzones/FancyZones.FuzzTests/FuzzTests.cs b/src/modules/fancyzones/FancyZones.FuzzTests/FuzzTests.cs new file mode 100644 index 0000000000..3bc846089f --- /dev/null +++ b/src/modules/fancyzones/FancyZones.FuzzTests/FuzzTests.cs @@ -0,0 +1,86 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Text.Json; +using FancyZonesEditorCommon.Data; +using FancyZonesEditorCommon.Utils; +using static FancyZonesEditorCommon.Data.CustomLayouts; + +namespace FancyZones.FuzzTests +{ + public class FuzzTests + { + public static void FuzzGridFromJsonElement(ReadOnlySpan input) + { + if (input.Length < 4) + { + return; + } + + int inputData = BitConverter.ToInt32(input.Slice(0, 4)); + + // mock user input for custom-layouts.json + string mockCustomLayouts = $@"{{""custom-layouts"": [{{ + ""uuid"": ""{{B8C275E-A7BC-485F-A35C-67B69164F51F}}"", + ""name"": ""Custom layout 1"", + ""type"": ""grid"", + ""info"": {{ + ""rows"": {inputData}, + ""columns"": {inputData}, + ""rows-percentage"": [ {inputData} ], + ""columns-percentage"": [ {inputData}, {inputData}, {inputData} ], + ""cell-child-map"": [ [{inputData}, {inputData}, {inputData}] ], + ""show-spacing"": true, + ""spacing"": {inputData}, + ""sensitivity-radius"": {inputData} + }} + }}]}}"; + + CustomLayoutListWrapper wrapper; + try + { + wrapper = JsonSerializer.Deserialize(mockCustomLayouts, JsonOptions); + } + catch (JsonException) + { + return; + } + + List customLayouts = wrapper.CustomLayouts; + + if (customLayouts == null) + { + return; + } + + // Get Layout Info from mockCustomLayouts + foreach (var zoneSet in customLayouts) + { + if (zoneSet.Uuid == null || zoneSet.Uuid.Length == 0) + { + return; + } + + CustomLayouts deserializer = new CustomLayouts(); + + // Fuzzing the deserializer + _ = deserializer.GridFromJsonElement(zoneSet.Info.GetRawText()); + } + } + + private static JsonSerializerOptions JsonOptions + { + get + { + return new JsonSerializerOptions + { + PropertyNamingPolicy = new DashCaseNamingPolicy(), + WriteIndented = true, + }; + } + } + } +} diff --git a/src/modules/fancyzones/FancyZones.FuzzTests/MSTestSettings.cs b/src/modules/fancyzones/FancyZones.FuzzTests/MSTestSettings.cs new file mode 100644 index 0000000000..5b05c0b86e --- /dev/null +++ b/src/modules/fancyzones/FancyZones.FuzzTests/MSTestSettings.cs @@ -0,0 +1,5 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)] diff --git a/src/modules/fancyzones/FancyZones.FuzzTests/OneFuzzConfig.json b/src/modules/fancyzones/FancyZones.FuzzTests/OneFuzzConfig.json new file mode 100644 index 0000000000..01abd5594e --- /dev/null +++ b/src/modules/fancyzones/FancyZones.FuzzTests/OneFuzzConfig.json @@ -0,0 +1,51 @@ +{ + "configVersion": 3, + "entries": [ + { + "fuzzer": { + "$type": "libfuzzerDotNet", + "dll": "FancyZones.FuzzTests.dll", + "class": "FancyZones.FuzzTests.FuzzTests", + "method": "FuzzGridFromJsonElement", + "FuzzingTargetBinaries": [ + "PowerToys.FancyZones.dll" + ] + }, + "adoTemplate": { + // supply the values appropriate to your + // project, where bugs will be filed + "org": "microsoft", + "project": "OS", + "AssignedTo": "mengyuanchen@microsoft.com", + "AreaPath": "OS\\Windows Client and Services\\WinPD\\DEEP-Developer Experience, Ecosystem and Partnerships\\SHINE\\PowerToys", + "IterationPath": "OS\\Future" + }, + "jobNotificationEmail": "mengyuanchen@microsoft.com", + "skip": false, + "rebootAfterSetup": false, + "oneFuzzJobs": [ + // at least one job is required + { + "projectName": "FancyZones", + "targetName": "FancyZones-dotnet-fuzzer-FuzzGridFromJsonElement" + } + ], + "jobDependencies": [ + // this should contain, at minimum, + // the DLL and PDB files + // you will need to add any other files required + // (globs are supported) + "FancyZones.FuzzTests.dll", + "FancyZones.FuzzTests.pdb", + "Microsoft.Windows.SDK.NET.dll", + "Newtonsoft.Json.dll", + "System.IO.Abstractions.dll", + "Testably.Abstractions.FileSystem.Interface.dll", + "TestableIO.System.IO.Abstractions.dll", + "TestableIO.System.IO.Abstractions.Wrappers.dll", + "WinRT.Runtime.dll" + ], + "SdlWorkItemId": 49911822 + } + ] +} \ No newline at end of file From d21b7fac7b3500d7af338641cfd9d7550cae3d11 Mon Sep 17 00:00:00 2001 From: Kai Tao <69313318+vanzue@users.noreply.github.com> Date: Wed, 28 May 2025 18:53:35 +0800 Subject: [PATCH 025/117] Log: Clear mwb module interface old version log and clear dotnet old version log (#39652) * Initial plan for issue * Add cleanup of old version log folders for Mouse Without Borders Co-authored-by: vanzue <69313318+vanzue@users.noreply.github.com> * Add try-catch block to cleanup_old_logs function Co-authored-by: vanzue <69313318+vanzue@users.noreply.github.com> * Refactor Mouse Without Borders log cleanup to use common mechanism Co-authored-by: vanzue <69313318+vanzue@users.noreply.github.com> * Investigate .NET logger cleanup mechanism for old version logs Co-authored-by: vanzue <69313318+vanzue@users.noreply.github.com> * delete in other thread * clear mwb previous folder * slow down the deletion --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> --- src/common/ManagedCommon/Logger.cs | 60 +++++++++++++++++-- .../ModuleInterface/dllmain.cpp | 10 +++- 2 files changed, 61 insertions(+), 9 deletions(-) diff --git a/src/common/ManagedCommon/Logger.cs b/src/common/ManagedCommon/Logger.cs index cd85d500e1..c034abb6e6 100644 --- a/src/common/ManagedCommon/Logger.cs +++ b/src/common/ManagedCommon/Logger.cs @@ -6,9 +6,10 @@ using System; using System.Diagnostics; using System.Globalization; using System.IO; +using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; - +using System.Threading.Tasks; using PowerToys.Interop; namespace ManagedCommon @@ -40,25 +41,72 @@ namespace ManagedCommon /// If the process using Logger is a low-privilege process. public static void InitializeLogger(string applicationLogPath, bool isLocalLow = false) { + string basePath; if (isLocalLow) { - applicationLogPath = Environment.GetEnvironmentVariable("userprofile") + "\\appdata\\LocalLow\\Microsoft\\PowerToys" + applicationLogPath + "\\" + Version; + basePath = Environment.GetEnvironmentVariable("userprofile") + "\\appdata\\LocalLow\\Microsoft\\PowerToys" + applicationLogPath; } else { - applicationLogPath = Constants.AppDataPath() + applicationLogPath + "\\" + Version; + basePath = Constants.AppDataPath() + applicationLogPath; } - if (!Directory.Exists(applicationLogPath)) + string versionedPath = Path.Combine(basePath, Version); + + if (!Directory.Exists(versionedPath)) { - Directory.CreateDirectory(applicationLogPath); + Directory.CreateDirectory(versionedPath); } - var logFilePath = Path.Combine(applicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".log"); + var logFilePath = Path.Combine(versionedPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".log"); Trace.Listeners.Add(new TextWriterTraceListener(logFilePath)); Trace.AutoFlush = true; + + // Clean up old version log folders + Task.Run(() => DeleteOldVersionLogFolders(basePath, versionedPath)); + } + + /// + /// Deletes old version log folders, keeping only the current version's folder. + /// + /// The base path to the log files folder. + /// The path to the current version's log folder. + private static void DeleteOldVersionLogFolders(string basePath, string currentVersionPath) + { + try + { + if (!Directory.Exists(basePath)) + { + return; + } + + var dirs = Directory.GetDirectories(basePath) + .Select(d => new DirectoryInfo(d)) + .OrderBy(d => d.CreationTime) + .Where(d => !string.Equals(d.FullName, currentVersionPath, StringComparison.OrdinalIgnoreCase)) + .Take(3) + .ToList(); + + foreach (var directory in dirs) + { + try + { + Directory.Delete(directory.FullName, true); + LogInfo($"Deleted old log directory: {directory.FullName}"); + Task.Delay(500).Wait(); + } + catch (Exception ex) + { + LogError($"Failed to delete old log directory: {directory.FullName}", ex); + } + } + } + catch (Exception ex) + { + LogError("Error cleaning up old log folders", ex); + } } public static void LogError(string message, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "", [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0) diff --git a/src/modules/MouseWithoutBorders/ModuleInterface/dllmain.cpp b/src/modules/MouseWithoutBorders/ModuleInterface/dllmain.cpp index 6bb823453f..33030fbdfb 100644 --- a/src/modules/MouseWithoutBorders/ModuleInterface/dllmain.cpp +++ b/src/modules/MouseWithoutBorders/ModuleInterface/dllmain.cpp @@ -13,6 +13,7 @@ #include #include #include +#include HINSTANCE g_hInst_MouseWithoutBorders = 0; @@ -409,9 +410,12 @@ public: { app_name = L"MouseWithoutBorders"; app_key = app_name; - std::filesystem::path logFilePath(PTSettingsHelper::get_module_save_folder_location(app_key)); - logFilePath.append(LogSettings::mouseWithoutBordersLogPath); - Logger::init(LogSettings::mouseWithoutBordersLoggerName, logFilePath.wstring(), PTSettingsHelper::get_log_settings_file_location()); + + LoggerHelpers::init_logger(app_key, L"ModuleInterface", LogSettings::mouseWithoutBordersLoggerName); + + std::filesystem::path oldLogPath(PTSettingsHelper::get_module_save_folder_location(app_key)); + oldLogPath.append("LogsModuleInterface"); + LoggerHelpers::delete_old_log_folder(oldLogPath); try { From 48d9d19df141e85a5a9a7689fdacafefe06f99d1 Mon Sep 17 00:00:00 2001 From: Michael Jolley Date: Wed, 28 May 2025 14:35:26 -0500 Subject: [PATCH 026/117] CmdPal: Styling critical context items using the SystemFillColorCriticalBrush (#39645) Styles context items based on the IsCritical property. ![image](https://github.com/user-attachments/assets/aa1ee0b0-de09-45af-8f5e-74dff666fbb4) ![image](https://github.com/user-attachments/assets/90a7f750-c949-4f76-b699-db2e29251414) Closes #38307 --- .../cmdpal/Microsoft.CmdPal.UI/App.xaml | 1 + .../Controls/CommandBar.xaml | 43 +++++++++++++++++-- .../Converters/ContextItemTemplateSelector.cs | 21 +++++++++ .../Microsoft.CmdPal.UI.csproj | 4 +- .../Microsoft.CmdPal.UI/Styles/TextBlock.xaml | 27 ++++++++++++ .../Components/ContextMenuHelper.cs | 1 + 6 files changed, 91 insertions(+), 6 deletions(-) create mode 100644 src/modules/cmdpal/Microsoft.CmdPal.UI/Converters/ContextItemTemplateSelector.cs create mode 100644 src/modules/cmdpal/Microsoft.CmdPal.UI/Styles/TextBlock.xaml diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/App.xaml b/src/modules/cmdpal/Microsoft.CmdPal.UI/App.xaml index ffcca6b3a8..9f4adee20a 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/App.xaml +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/App.xaml @@ -11,6 +11,7 @@ + diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/CommandBar.xaml b/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/CommandBar.xaml index 203009f763..d4c4f49879 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/CommandBar.xaml +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/CommandBar.xaml @@ -34,8 +34,13 @@ Orientation="Vertical" Spacing="4" /> - - + + + + @@ -63,6 +68,38 @@ Text="{x:Bind RequestedShortcut, Mode=OneWay, Converter={StaticResource KeyChordToStringConverter}}" /> + + + + + + + + + + + + + + + @@ -237,7 +274,7 @@ Margin="-16,-12,-16,-12" IsItemClickEnabled="True" ItemClick="CommandsDropdown_ItemClick" - ItemTemplate="{StaticResource ContextMenuViewModelTemplate}" + ItemTemplateSelector="{StaticResource ContextItemTemplateSelector}" ItemsSource="{x:Bind ViewModel.ContextMenu.FilteredItems, Mode=OneWay}" KeyDown="CommandsDropdown_KeyDown" SelectionMode="Single"> diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Converters/ContextItemTemplateSelector.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/Converters/ContextItemTemplateSelector.cs new file mode 100644 index 0000000000..b9ff7c3439 --- /dev/null +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Converters/ContextItemTemplateSelector.cs @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CmdPal.UI.ViewModels; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; + +namespace Microsoft.CmdPal.UI; + +internal sealed partial class ContextItemTemplateSelector : DataTemplateSelector +{ + public DataTemplate? Default { get; set; } + + public DataTemplate? Critical { get; set; } + + protected override DataTemplate? SelectTemplateCore(object item) + { + return ((CommandContextItemViewModel)item).IsCritical ? Critical : Default; + } +} diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Microsoft.CmdPal.UI.csproj b/src/modules/cmdpal/Microsoft.CmdPal.UI/Microsoft.CmdPal.UI.csproj index b3ec6188cf..0b653d2d0a 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Microsoft.CmdPal.UI.csproj +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Microsoft.CmdPal.UI.csproj @@ -39,9 +39,7 @@ - + diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Styles/TextBlock.xaml b/src/modules/cmdpal/Microsoft.CmdPal.UI/Styles/TextBlock.xaml new file mode 100644 index 0000000000..6160585127 --- /dev/null +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Styles/TextBlock.xaml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Components/ContextMenuHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Components/ContextMenuHelper.cs index cbadadc699..a2064b0369 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Components/ContextMenuHelper.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Components/ContextMenuHelper.cs @@ -36,6 +36,7 @@ internal sealed class ContextMenuHelper contextMenu.Add(new CommandContextItem(new KillProcessCommand(windowData)) { RequestedShortcut = KeyChordHelpers.FromModifiers(true, false, false, false, (int)VirtualKey.Delete, 0), + IsCritical = true, }); } From 964e5c9d5eef66c4d7cc9770c87eba0a8ff22ba0 Mon Sep 17 00:00:00 2001 From: Royce Williams Date: Wed, 28 May 2025 13:13:05 -0700 Subject: [PATCH 027/117] cmdpal README: minor typo (#39764) ## Summary of the Pull Request Minor typo it's -> its ## PR Checklist ## Detailed Description of the Pull Request / Additional comments README-only change ## Validation Steps Performed n/a --- src/modules/cmdpal/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/cmdpal/README.md b/src/modules/cmdpal/README.md index 518a54b78f..d0c626e3f1 100644 --- a/src/modules/cmdpal/README.md +++ b/src/modules/cmdpal/README.md @@ -1,6 +1,6 @@ # ![cmdpal logo](./Microsoft.CmdPal.UI/Assets/Stable/StoreLogo.scale-100.png) Command Palette -Windows Command Palette ("CmdPal") is the next iteration of PowerToys Run. With extensibility at it's core, the Command Palette is your one-stop launcher to start _anything_. +Windows Command Palette ("CmdPal") is the next iteration of PowerToys Run. With extensibility at its core, the Command Palette is your one-stop launcher to start _anything_. By default, CmdPal is bound to Win+Alt+Space. From 3821e8427ce211784b35cb42f445000f3c81cfe1 Mon Sep 17 00:00:00 2001 From: zhw Date: Thu, 29 May 2025 18:09:05 +0800 Subject: [PATCH 028/117] [Localization] Add comments in Resources.resw (#39778) ## Summary of the Pull Request This PR mainly add comments in src/modules/AdvancedPaste/AdvancedPaste/Strings/en-us/Resources.resw to solve issues raised by localization team. --------- Co-authored-by: ArleneYu --- .../AdvancedPaste/AdvancedPaste/Strings/en-us/Resources.resw | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/modules/AdvancedPaste/AdvancedPaste/Strings/en-us/Resources.resw b/src/modules/AdvancedPaste/AdvancedPaste/Strings/en-us/Resources.resw index 30b46190e3..604cbf403b 100644 --- a/src/modules/AdvancedPaste/AdvancedPaste/Strings/en-us/Resources.resw +++ b/src/modules/AdvancedPaste/AdvancedPaste/Strings/en-us/Resources.resw @@ -152,6 +152,7 @@ Image data + Label used to represent an image in the clipboard history More options @@ -194,15 +195,19 @@ Transcode to .mp3 + Option to transcode audio files to MP3 format Transcode to .mp4 (H.264/AAC) + Option to transcode video files to MP4 format with H.264 video codec and AAC audio codec An error occurred while transcoding media file + Error message displayed when media conversion fails for an unknown or general reason The media file contains an unsupported codec + Error message displayed when media conversion fails due to an unsupported codec in the source file Paste From 498ef676f41990d4ec40b25727b2ad06986cca8d Mon Sep 17 00:00:00 2001 From: Kai Tao <69313318+vanzue@users.noreply.github.com> Date: Fri, 30 May 2025 10:03:36 +0800 Subject: [PATCH 029/117] Runner: Add log when error happen in setting startup (#39801) ## Summary of the Pull Request Add log when error happen in setting startup ## PR Checklist - [X] **Closes:** #39798 - [ ] **Communication:** I've discussed this with core contributors already. If work hasn't been agreed, this work might be rejected - [X] **Tests:** Added/updated and all pass - [ ] **Localization:** All end user facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx ## Detailed Description of the Pull Request / Additional comments ## Validation Steps Performed ![image](https://github.com/user-attachments/assets/2208c65f-4d42-4612-a77e-d92a748fd723) Set error in code deliberately, and verify that the error appears in the log. --- src/runner/auto_start_helper.cpp | 70 ++++++++++++++++---------------- 1 file changed, 36 insertions(+), 34 deletions(-) diff --git a/src/runner/auto_start_helper.cpp b/src/runner/auto_start_helper.cpp index 56e11f1c6e..825abe5f6c 100644 --- a/src/runner/auto_start_helper.cpp +++ b/src/runner/auto_start_helper.cpp @@ -5,12 +5,13 @@ #include #include +#include // Helper macros from wix. -// TODO: use "s" and "..." parameters to report errors from these functions. #define ExitOnFailure(x, s, ...) \ if (FAILED(x)) \ { \ + Logger::error(s, ##__VA_ARGS__); \ goto LExit; \ } #define ExitWithLastError(x, s, ...) \ @@ -21,6 +22,7 @@ { \ x = E_FAIL; \ } \ + Logger::error(s, ##__VA_ARGS__); \ goto LExit; \ } #define ExitFunction() \ @@ -52,11 +54,11 @@ bool create_auto_start_task_for_this_user(bool runElevated) // Get the Domain/Username for the trigger. if (!GetEnvironmentVariable(L"USERNAME", username, USERNAME_LEN)) { - ExitWithLastError(hr, "Getting username failed: %x", hr); + ExitWithLastError(hr, "Getting username failed: {:x}", hr); } if (!GetEnvironmentVariable(L"USERDOMAIN", username_domain, USERNAME_DOMAIN_LEN)) { - ExitWithLastError(hr, "Getting the user's domain failed: %x", hr); + ExitWithLastError(hr, "Getting the user's domain failed: {:x}", hr); } wcscat_s(username_domain, L"\\"); wcscat_s(username_domain, username); @@ -76,11 +78,11 @@ bool create_auto_start_task_for_this_user(bool runElevated) CLSCTX_INPROC_SERVER, IID_ITaskService, reinterpret_cast(&pService)); - ExitOnFailure(hr, "Failed to create an instance of ITaskService: %x", hr); + ExitOnFailure(hr, "Failed to create an instance of ITaskService: {:x}", hr); // Connect to the task service. hr = pService->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t()); - ExitOnFailure(hr, "ITaskService::Connect failed: %x", hr); + ExitOnFailure(hr, "ITaskService::Connect failed: {:x}", hr); // ------------------------------------------------------ // Get the PowerToys task folder. Creates it if it doesn't exist. @@ -90,12 +92,12 @@ bool create_auto_start_task_for_this_user(bool runElevated) // Folder doesn't exist. Get the Root folder and create the PowerToys subfolder. ITaskFolder* pRootFolder = NULL; hr = pService->GetFolder(_bstr_t(L"\\"), &pRootFolder); - ExitOnFailure(hr, "Cannot get Root Folder pointer: %x", hr); + ExitOnFailure(hr, "Cannot get Root Folder pointer: {:x}", hr); hr = pRootFolder->CreateFolder(_bstr_t(L"\\PowerToys"), _variant_t(L""), &pTaskFolder); if (FAILED(hr)) { pRootFolder->Release(); - ExitOnFailure(hr, "Cannot create PowerToys task folder: %x", hr); + ExitOnFailure(hr, "Cannot create PowerToys task folder: {:x}", hr); } } @@ -118,47 +120,47 @@ bool create_auto_start_task_for_this_user(bool runElevated) // Create the task builder object to create the task. hr = pService->NewTask(0, &pTask); - ExitOnFailure(hr, "Failed to create a task definition: %x", hr); + ExitOnFailure(hr, "Failed to create a task definition: {:x}", hr); // ------------------------------------------------------ // Get the registration info for setting the identification. hr = pTask->get_RegistrationInfo(&pRegInfo); - ExitOnFailure(hr, "Cannot get identification pointer: %x", hr); + ExitOnFailure(hr, "Cannot get identification pointer: {:x}", hr); hr = pRegInfo->put_Author(_bstr_t(username_domain)); - ExitOnFailure(hr, "Cannot put identification info: %x", hr); + ExitOnFailure(hr, "Cannot put identification info: {:x}", hr); // ------------------------------------------------------ // Create the settings for the task hr = pTask->get_Settings(&pSettings); - ExitOnFailure(hr, "Cannot get settings pointer: %x", hr); + ExitOnFailure(hr, "Cannot get settings pointer: {:x}", hr); hr = pSettings->put_StartWhenAvailable(VARIANT_FALSE); - ExitOnFailure(hr, "Cannot put_StartWhenAvailable setting info: %x", hr); + ExitOnFailure(hr, "Cannot put_StartWhenAvailable setting info: {:x}", hr); hr = pSettings->put_StopIfGoingOnBatteries(VARIANT_FALSE); - ExitOnFailure(hr, "Cannot put_StopIfGoingOnBatteries setting info: %x", hr); + ExitOnFailure(hr, "Cannot put_StopIfGoingOnBatteries setting info: {:x}", hr); hr = pSettings->put_ExecutionTimeLimit(_bstr_t(L"PT0S")); //Unlimited - ExitOnFailure(hr, "Cannot put_ExecutionTimeLimit setting info: %x", hr); + ExitOnFailure(hr, "Cannot put_ExecutionTimeLimit setting info: {:x}", hr); hr = pSettings->put_DisallowStartIfOnBatteries(VARIANT_FALSE); - ExitOnFailure(hr, "Cannot put_DisallowStartIfOnBatteries setting info: %x", hr); + ExitOnFailure(hr, "Cannot put_DisallowStartIfOnBatteries setting info: {:x}", hr); hr = pSettings->put_Priority(4); - ExitOnFailure(hr, "Cannot put_Priority setting info : %x", hr); + ExitOnFailure(hr, "Cannot put_Priority setting info : {:x}", hr); // ------------------------------------------------------ // Get the trigger collection to insert the logon trigger. hr = pTask->get_Triggers(&pTriggerCollection); - ExitOnFailure(hr, "Cannot get trigger collection: %x", hr); + ExitOnFailure(hr, "Cannot get trigger collection: {:x}", hr); // Add the logon trigger to the task. { ITrigger* pTrigger = NULL; ILogonTrigger* pLogonTrigger = NULL; hr = pTriggerCollection->Create(TASK_TRIGGER_LOGON, &pTrigger); - ExitOnFailure(hr, "Cannot create the trigger: %x", hr); + ExitOnFailure(hr, "Cannot create the trigger: {:x}", hr); hr = pTrigger->QueryInterface( IID_ILogonTrigger, reinterpret_cast(&pLogonTrigger)); pTrigger->Release(); - ExitOnFailure(hr, "QueryInterface call failed for ILogonTrigger: %x", hr); + ExitOnFailure(hr, "QueryInterface call failed for ILogonTrigger: {:x}", hr); hr = pLogonTrigger->put_Id(_bstr_t(L"Trigger1")); @@ -170,7 +172,7 @@ bool create_auto_start_task_for_this_user(bool runElevated) // The specified user must be a user on this computer. hr = pLogonTrigger->put_UserId(_bstr_t(username_domain)); pLogonTrigger->Release(); - ExitOnFailure(hr, "Cannot add user ID to logon trigger: %x", hr); + ExitOnFailure(hr, "Cannot add user ID to logon trigger: {:x}", hr); } // ------------------------------------------------------ @@ -182,23 +184,23 @@ bool create_auto_start_task_for_this_user(bool runElevated) // Get the task action collection pointer. hr = pTask->get_Actions(&pActionCollection); - ExitOnFailure(hr, "Cannot get Task collection pointer: %x", hr); + ExitOnFailure(hr, "Cannot get Task collection pointer: {:x}", hr); // Create the action, specifying that it is an executable action. hr = pActionCollection->Create(TASK_ACTION_EXEC, &pAction); pActionCollection->Release(); - ExitOnFailure(hr, "Cannot create the action: %x", hr); + ExitOnFailure(hr, "Cannot create the action: {:x}", hr); // QI for the executable task pointer. hr = pAction->QueryInterface( IID_IExecAction, reinterpret_cast(&pExecAction)); pAction->Release(); - ExitOnFailure(hr, "QueryInterface call failed for IExecAction: %x", hr); + ExitOnFailure(hr, "QueryInterface call failed for IExecAction: {:x}", hr); // Set the path of the executable to PowerToys (passed as CustomActionData). hr = pExecAction->put_Path(_bstr_t(wszExecutablePath)); pExecAction->Release(); - ExitOnFailure(hr, "Cannot set path of executable: %x", hr); + ExitOnFailure(hr, "Cannot set path of executable: {:x}", hr); } // ------------------------------------------------------ @@ -206,7 +208,7 @@ bool create_auto_start_task_for_this_user(bool runElevated) { IPrincipal* pPrincipal = NULL; hr = pTask->get_Principal(&pPrincipal); - ExitOnFailure(hr, "Cannot get principal pointer: %x", hr); + ExitOnFailure(hr, "Cannot get principal pointer: {:x}", hr); // Set up principal information: hr = pPrincipal->put_Id(_bstr_t(L"Principal1")); @@ -224,7 +226,7 @@ bool create_auto_start_task_for_this_user(bool runElevated) hr = pPrincipal->put_RunLevel(_TASK_RUNLEVEL::TASK_RUNLEVEL_LUA); } pPrincipal->Release(); - ExitOnFailure(hr, "Cannot put principal run level: %x", hr); + ExitOnFailure(hr, "Cannot put principal run level: {:x}", hr); } // ------------------------------------------------------ // Save the task in the PowerToys folder. @@ -239,7 +241,7 @@ bool create_auto_start_task_for_this_user(bool runElevated) TASK_LOGON_INTERACTIVE_TOKEN, SDDL_FULL_ACCESS_FOR_EVERYONE, &pRegisteredTask); - ExitOnFailure(hr, "Error saving the Task : %x", hr); + ExitOnFailure(hr, "Error saving the Task : {:x}", hr); } LExit: @@ -275,7 +277,7 @@ bool delete_auto_start_task_for_this_user() // Get the Username for the task. if (!GetEnvironmentVariable(L"USERNAME", username, USERNAME_LEN)) { - ExitWithLastError(hr, "Getting username failed: %x", hr); + ExitWithLastError(hr, "Getting username failed: {:x}", hr); } // Task Name. @@ -289,11 +291,11 @@ bool delete_auto_start_task_for_this_user() CLSCTX_INPROC_SERVER, IID_ITaskService, reinterpret_cast(&pService)); - ExitOnFailure(hr, "Failed to create an instance of ITaskService: %x", hr); + ExitOnFailure(hr, "Failed to create an instance of ITaskService: {:x}", hr); // Connect to the task service. hr = pService->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t()); - ExitOnFailure(hr, "ITaskService::Connect failed: %x", hr); + ExitOnFailure(hr, "ITaskService::Connect failed: {:x}", hr); // ------------------------------------------------------ // Get the PowerToys task folder. @@ -340,7 +342,7 @@ bool is_auto_start_task_active_for_this_user() // Get the Username for the task. if (!GetEnvironmentVariable(L"USERNAME", username, USERNAME_LEN)) { - ExitWithLastError(hr, "Getting username failed: %x", hr); + ExitWithLastError(hr, "Getting username failed: {:x}", hr); } // Task Name. @@ -354,16 +356,16 @@ bool is_auto_start_task_active_for_this_user() CLSCTX_INPROC_SERVER, IID_ITaskService, reinterpret_cast(&pService)); - ExitOnFailure(hr, "Failed to create an instance of ITaskService: %x", hr); + ExitOnFailure(hr, "Failed to create an instance of ITaskService: {:x}", hr); // Connect to the task service. hr = pService->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t()); - ExitOnFailure(hr, "ITaskService::Connect failed: %x", hr); + ExitOnFailure(hr, "ITaskService::Connect failed: {:x}", hr); // ------------------------------------------------------ // Get the PowerToys task folder. hr = pService->GetFolder(_bstr_t(L"\\PowerToys"), &pTaskFolder); - ExitOnFailure(hr, "ITaskFolder doesn't exist: %x", hr); + ExitOnFailure(hr, "ITaskFolder doesn't exist: {:x}", hr); // ------------------------------------------------------ // If the task exists, disable. From c9922302e56eabfbf7818169b61528771c218210 Mon Sep 17 00:00:00 2001 From: Yu Leng <42196638+moooyo@users.noreply.github.com> Date: Fri, 30 May 2025 13:07:20 +0800 Subject: [PATCH 030/117] [PowerRename] Add ModificationTime and AccessTime support when renaming with $YY-$MM-$DD patterns (#39653) ## Summary of the Pull Request 1. Add new configuration to control this behaviour. By default, set to creation time to align with previous version 2. Add UI in PowerRename's MainWindow 3. Implement the logic ![image](https://github.com/user-attachments/assets/fde6731b-73f9-453f-8b68-6ce66589f44a) Original discussion here: https://github.com/microsoft/PowerToys/pull/38186 ## PR Checklist - [x] **Closes:** #36040 - [x] **Communication:** I've discussed this with core contributors already. If work hasn't been agreed, this work might be rejected - [x] **Tests:** Added/updated and all pass - [x] **Localization:** All end user facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx ## Detailed Description of the Pull Request / Additional comments ## Validation Steps Performed --------- Co-authored-by: Yu Leng (from Dev Box) Co-authored-by: Heiko <61519853+htcfreek@users.noreply.github.com> --- .../PowerRenameXAML/MainWindow.xaml | 28 +++++++++++ .../PowerRenameXAML/MainWindow.xaml.cpp | 26 ++++++++++ .../Strings/en-us/Resources.resw | 12 +++++ .../powerrename/lib/PowerRenameInterfaces.h | 7 ++- .../powerrename/lib/PowerRenameItem.cpp | 48 +++++++++++++++++-- src/modules/powerrename/lib/PowerRenameItem.h | 3 +- src/modules/powerrename/lib/Renaming.cpp | 2 +- 7 files changed, 117 insertions(+), 9 deletions(-) diff --git a/src/modules/powerrename/PowerRenameUILib/PowerRenameXAML/MainWindow.xaml b/src/modules/powerrename/PowerRenameUILib/PowerRenameXAML/MainWindow.xaml index d049a0d067..7126a63604 100644 --- a/src/modules/powerrename/PowerRenameUILib/PowerRenameXAML/MainWindow.xaml +++ b/src/modules/powerrename/PowerRenameUILib/PowerRenameXAML/MainWindow.xaml @@ -184,6 +184,7 @@ + + + + + + + + + + + + Random string features + + Time used for replacement + + + Creation Time + + + Modification Time + + + Access Time + \ No newline at end of file diff --git a/src/modules/powerrename/lib/PowerRenameInterfaces.h b/src/modules/powerrename/lib/PowerRenameInterfaces.h index c545e9dc00..ea761d156a 100644 --- a/src/modules/powerrename/lib/PowerRenameInterfaces.h +++ b/src/modules/powerrename/lib/PowerRenameInterfaces.h @@ -18,7 +18,10 @@ enum PowerRenameFlags Lowercase = 0x400, Titlecase = 0x800, Capitalized = 0x1000, - RandomizeItems = 0x2000 + RandomizeItems = 0x2000, + CreationTime = 0x4000, + ModificationTime = 0x8000, + AccessTime = 0x10000, }; enum PowerRenameFilters @@ -67,7 +70,7 @@ interface __declspec(uuid("C7F59201-4DE1-4855-A3A2-26FC3279C8A5")) IPowerRenameI public: IFACEMETHOD(PutPath)(_In_opt_ PCWSTR newPath) = 0; IFACEMETHOD(GetPath)(_Outptr_ PWSTR * path) = 0; - IFACEMETHOD(GetTime)(_Outptr_ SYSTEMTIME* time) = 0; + IFACEMETHOD(GetTime)(_In_ DWORD flags, _Outptr_ SYSTEMTIME * time) = 0; IFACEMETHOD(GetShellItem)(_Outptr_ IShellItem** ppsi) = 0; IFACEMETHOD(GetOriginalName)(_Outptr_ PWSTR * originalName) = 0; IFACEMETHOD(PutOriginalName)(_In_opt_ PCWSTR originalName) = 0; diff --git a/src/modules/powerrename/lib/PowerRenameItem.cpp b/src/modules/powerrename/lib/PowerRenameItem.cpp index bd8fa48285..9a0652c451 100644 --- a/src/modules/powerrename/lib/PowerRenameItem.cpp +++ b/src/modules/powerrename/lib/PowerRenameItem.cpp @@ -56,12 +56,28 @@ IFACEMETHODIMP CPowerRenameItem::GetPath(_Outptr_ PWSTR* path) return hr; } -IFACEMETHODIMP CPowerRenameItem::GetTime(_Outptr_ SYSTEMTIME* time) +IFACEMETHODIMP CPowerRenameItem::GetTime(_In_ DWORD flags, _Outptr_ SYSTEMTIME* time) { CSRWSharedAutoLock lock(&m_lock); HRESULT hr = E_FAIL; + PowerRenameFlags parsedTimeType; - if (m_isTimeParsed) + // Get Time by PowerRenameFlags + if (flags & PowerRenameFlags::ModificationTime) + { + parsedTimeType = PowerRenameFlags::ModificationTime; + } + else if (flags & PowerRenameFlags::AccessTime) + { + parsedTimeType = PowerRenameFlags::AccessTime; + } + else + { + // Default to modification time if no specific flag is set + parsedTimeType = PowerRenameFlags::CreationTime; + } + + if (m_isTimeParsed && parsedTimeType == m_parsedTimeType) { hr = S_OK; } @@ -70,21 +86,43 @@ IFACEMETHODIMP CPowerRenameItem::GetTime(_Outptr_ SYSTEMTIME* time) HANDLE hFile = CreateFileW(m_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (hFile != INVALID_HANDLE_VALUE) { - FILETIME CreationTime; - if (GetFileTime(hFile, &CreationTime, NULL, NULL)) + FILETIME FileTime; + bool success = false; + + // Get Time by PowerRenameFlags + switch (parsedTimeType) + { + case PowerRenameFlags::CreationTime: + success = GetFileTime(hFile, &FileTime, NULL, NULL); + break; + case PowerRenameFlags::ModificationTime: + success = GetFileTime(hFile, NULL, NULL, &FileTime); + break; + case PowerRenameFlags::AccessTime: + success = GetFileTime(hFile, NULL, &FileTime, NULL); + break; + default: + // Default to modification time if no specific flag is set + success = GetFileTime(hFile, NULL, NULL, &FileTime); + break; + } + + if (success) { SYSTEMTIME SystemTime, LocalTime; - if (FileTimeToSystemTime(&CreationTime, &SystemTime)) + if (FileTimeToSystemTime(&FileTime, &SystemTime)) { if (SystemTimeToTzSpecificLocalTime(NULL, &SystemTime, &LocalTime)) { m_time = LocalTime; m_isTimeParsed = true; + m_parsedTimeType = parsedTimeType; hr = S_OK; } } } } + CloseHandle(hFile); } *time = m_time; diff --git a/src/modules/powerrename/lib/PowerRenameItem.h b/src/modules/powerrename/lib/PowerRenameItem.h index 83817b0699..6ced0a3ada 100644 --- a/src/modules/powerrename/lib/PowerRenameItem.h +++ b/src/modules/powerrename/lib/PowerRenameItem.h @@ -16,7 +16,7 @@ public: // IPowerRenameItem IFACEMETHODIMP PutPath(_In_opt_ PCWSTR newPath); IFACEMETHODIMP GetPath(_Outptr_ PWSTR* path); - IFACEMETHODIMP GetTime(_Outptr_ SYSTEMTIME* time); + IFACEMETHODIMP GetTime(_In_ DWORD flags, _Outptr_ SYSTEMTIME* time); IFACEMETHODIMP GetShellItem(_Outptr_ IShellItem** ppsi); IFACEMETHODIMP PutOriginalName(_In_opt_ PCWSTR originalName); IFACEMETHODIMP GetOriginalName(_Outptr_ PWSTR* originalName); @@ -54,6 +54,7 @@ protected: bool m_selected = true; bool m_isFolder = false; bool m_isTimeParsed = false; + PowerRenameFlags m_parsedTimeType = PowerRenameFlags::CreationTime; bool m_canRename = true; int m_id = -1; int m_iconIndex = -1; diff --git a/src/modules/powerrename/lib/Renaming.cpp b/src/modules/powerrename/lib/Renaming.cpp index bf27500529..aa21666783 100644 --- a/src/modules/powerrename/lib/Renaming.cpp +++ b/src/modules/powerrename/lib/Renaming.cpp @@ -78,7 +78,7 @@ bool DoRename(CComPtr& spRenameRegEx, unsigned long& itemEnum if (useFileTime) { - winrt::check_hresult(spItem->GetTime(&fileTime)); + winrt::check_hresult(spItem->GetTime(flags, &fileTime)); winrt::check_hresult(spRenameRegEx->PutFileTime(fileTime)); } From 89c33fae6841dd9c35e62cd6e32e9dfeffe0d67e Mon Sep 17 00:00:00 2001 From: mbartlett21 <29034492+mbartlett21@users.noreply.github.com> Date: Fri, 30 May 2025 20:32:40 +1000 Subject: [PATCH 031/117] [peek, explorer] Update QOI reader for 3-channel images (#39304) ## Summary of the Pull Request Make sure we account for Bitmap row length being a multiple of 4 when reading Qoi files. ## PR Checklist - [x] **Closes:** #31948 - [ ] **Communication:** Not discussed, but a bug fix - [ ] **Tests:** Added/updated and all pass - [ ] **Localization:** All end user facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx ## Detailed Description of the Pull Request / Additional comments ## Validation Steps Performed Built and run and restarted file explorer. Sample files from my comment on the issue (after rewriting each one to force the thumbnail to regenrate): ![image](https://github.com/user-attachments/assets/5874f958-0f03-4683-abe3-d54c979ad2a1) --- src/common/FilePreviewCommon/QoiImage.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/common/FilePreviewCommon/QoiImage.cs b/src/common/FilePreviewCommon/QoiImage.cs index ac315eb832..fd72cb0e29 100644 --- a/src/common/FilePreviewCommon/QoiImage.cs +++ b/src/common/FilePreviewCommon/QoiImage.cs @@ -92,6 +92,9 @@ namespace Microsoft.PowerToys.FilePreviewCommon var run = 0; var chunksLen = fileSize - QOI_PADDING_LENGTH; + var x = 0; + var rowAdd = bitmapData.Stride - (channels * bitmapData.Width); + for (var dataIndex = 0; dataIndex < dataLength; dataIndex += channels) { if (run > 0) @@ -153,6 +156,14 @@ namespace Microsoft.PowerToys.FilePreviewCommon bitmapPixel[3] = pixel.A; } } + + x++; + if (x == bitmapData.Width) + { + // We align dataIndex with the stride + dataIndex += rowAdd; + x = 0; + } } bitmap.UnlockBits(bitmapData); From c6a4ee1441bea435bf4b47dffda3c17ee648a2b5 Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Mon, 2 Jun 2025 15:47:56 -0500 Subject: [PATCH 032/117] build: try even harder to find a working VC tools version (#39862) I can't explain this, but VS 17.14 ships with VisualCpp.Tools.Core version 14.44.35208 but the files say 14.44.35207. See https://github.com/microsoft/terminal/pull/18996 for more info. --- .../verifyAndSetLatestVCToolsVersion.ps1 | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/.pipelines/verifyAndSetLatestVCToolsVersion.ps1 b/.pipelines/verifyAndSetLatestVCToolsVersion.ps1 index a47e6a2292..4ebf3a3eec 100644 --- a/.pipelines/verifyAndSetLatestVCToolsVersion.ps1 +++ b/.pipelines/verifyAndSetLatestVCToolsVersion.ps1 @@ -1,7 +1,28 @@ $VSInstances = ([xml](& 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe' -latest -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -include packages -format xml)) $VSPackages = $VSInstances.instances.instance.packages.package -$LatestVCPackage = ($VSInstances.instances.instance.packages.package | ? { $_.id -eq "Microsoft.VisualCpp.Tools.Core" }) +$LatestVCPackage = ($VSPackages | ? { $_.id -eq "Microsoft.VisualCpp.Tools.Core" }) $LatestVCToolsVersion = $LatestVCPackage.version; + +$VSRoot = (& 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe' -latest -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property 'resolvedInstallationPath') +$VCToolsRoot = Join-Path $VSRoot "VC\Tools\MSVC" + +# We have observed a few instances where the VC tools package version actually +# differs from the version on the files themselves. We might as well check +# whether the version we just found _actually exists_ before we use it. +# We'll use whichever highest version exists. +$PackageVCToolPath = Join-Path $VCToolsRoot $LatestVCToolsVersion +If ($Null -Eq (Get-Item $PackageVCToolPath -ErrorAction:Ignore)) { + $VCToolsVersions = Get-ChildItem $VCToolsRoot | ForEach-Object { + [Version]$_.Name + } | Sort -Descending + $LatestActualVCToolsVersion = $VCToolsVersions | Select -First 1 + + If ([Version]$LatestVCToolsVersion -Ne $LatestActualVCToolsVersion) { + Write-Output "VC Tools Mismatch: Directory = $LatestActualVCToolsVersion, Package = $LatestVCToolsVersion" + $LatestVCToolsVersion = $LatestActualVCToolsVersion.ToString(3) + } +} + Write-Output "Latest VCToolsVersion: $LatestVCToolsVersion" Write-Output "Updating VCToolsVersion environment variable for job" Write-Output "##vso[task.setvariable variable=VCToolsVersion]$LatestVCToolsVersion" From 693ab67831e5b5b3d26544ff7260d674c0be3b43 Mon Sep 17 00:00:00 2001 From: Yu Leng <42196638+moooyo@users.noreply.github.com> Date: Tue, 3 Jun 2025 14:24:13 +0800 Subject: [PATCH 033/117] [cmdpal][AOT] make some built-in extensions able to be published with native AOT enabled (#39802) ## Summary of the Pull Request Base on my test, some built-in extensions can be published with native AOT enabled with a little changes. (see this branch: https://github.com/microsoft/PowerToys/pull/39605). 1. SystemCommands: no change need. Removed some unused code. 2. WinGet: disable marshalling. and do a little changes in CreateInstance. 3. WindowWalker: use source generation to call CoCreateInstance. 4. WindowsTerminal: use source generation to call IApplicationActivationManager interface. ## PR Checklist - [x] **Closes:** #39869 - [x] **Communication:** I've discussed this with core contributors already. If work hasn't been agreed, this work might be rejected - [x] **Tests:** Added/updated and all pass - [x] **Localization:** All end user facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx ## Detailed Description of the Pull Request / Additional comments ## Validation Steps Performed --------- Co-authored-by: Yu Leng --- .github/actions/spell-check/expect.txt | 1 + .../Helpers/Native.cs | 144 ------------------ .../Microsoft.CmdPal.Ext.WinGet.csproj | 5 + .../NativeMethods.json | 7 + .../WindowsPackageManagerStandardFactory.cs | 29 ++-- .../Helpers/IVirtualDesktopManager.cs | 13 +- .../Helpers/NativeMethods.cs | 68 ++------- .../Helpers/VirtualDesktopHelper.cs | 27 +++- .../Commands/LaunchProfileAsAdminCommand.cs | 28 ++-- .../Commands/LaunchProfileCommand.cs | 27 ++-- .../Helpers/ApplicationActivationManager.cs | 24 --- .../Helpers/IApplicationActivationManager.cs | 12 +- .../Helpers/NativeHelpers.cs | 32 ++++ 13 files changed, 142 insertions(+), 275 deletions(-) delete mode 100644 src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Helpers/Native.cs create mode 100644 src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/NativeMethods.json delete mode 100644 src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/ApplicationActivationManager.cs create mode 100644 src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/NativeHelpers.cs diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index f9d35feab1..ad79e2e8ca 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -1995,3 +1995,4 @@ culori Evercoder LCh CIELCh +CLSCTXINPROCALL \ No newline at end of file diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Helpers/Native.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Helpers/Native.cs deleted file mode 100644 index 3daf2a4be4..0000000000 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Helpers/Native.cs +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.InteropServices; - -namespace Microsoft.CmdPal.Ext.System.Helpers; - -[SuppressMessage("Interoperability", "CA1401:P/Invokes should not be visible", Justification = "We want plugins to share this NativeMethods class, instead of each one creating its own.")] -public sealed class Native -{ - public enum HRESULT : uint - { - /// - /// Operation successful. - /// - S_OK = 0x00000000, - - /// - /// Operation successful. (negative condition/no operation) - /// - S_FALSE = 0x00000001, - - /// - /// Not implemented. - /// - E_NOTIMPL = 0x80004001, - - /// - /// No such interface supported. - /// - E_NOINTERFACE = 0x80004002, - - /// - /// Pointer that is not valid. - /// - E_POINTER = 0x80004003, - - /// - /// Operation aborted. - /// - E_ABORT = 0x80004004, - - /// - /// Unspecified failure. - /// - E_FAIL = 0x80004005, - - /// - /// Unexpected failure. - /// - E_UNEXPECTED = 0x8000FFFF, - - /// - /// General access denied error. - /// - E_ACCESSDENIED = 0x80070005, - - /// - /// Handle that is not valid. - /// - E_HANDLE = 0x80070006, - - /// - /// Failed to allocate necessary memory. - /// - E_OUTOFMEMORY = 0x8007000E, - - /// - /// One or more arguments are not valid. - /// - E_INVALIDARG = 0x80070057, - - /// - /// The operation was canceled by the user. (Error source 7 means Win32.) - /// - /// - /// - E_CANCELLED = 0x800704C7, - } - - public static class ShellItemTypeConstants - { - /// - /// Guid for type IShellItem. - /// - public static readonly Guid ShellItemGuid = new("43826d1e-e718-42ee-bc55-a1e261c37bfe"); - - /// - /// Guid for type IShellItem2. - /// - public static readonly Guid ShellItem2Guid = new("7E9FB0D3-919F-4307-AB2E-9B1860310C93"); - } - - /// - /// The following are ShellItem DisplayName types. - /// - [Flags] - public enum SIGDN : uint - { - NORMALDISPLAY = 0, - PARENTRELATIVEPARSING = 0x80018001, - PARENTRELATIVEFORADDRESSBAR = 0x8001c001, - DESKTOPABSOLUTEPARSING = 0x80028000, - PARENTRELATIVEEDITING = 0x80031001, - DESKTOPABSOLUTEEDITING = 0x8004c000, - FILESYSPATH = 0x80058000, - URL = 0x80068000, - } - - [ComImport] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - [Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe")] - public interface IShellItem - { - void BindToHandler( - nint pbc, - [MarshalAs(UnmanagedType.LPStruct)] Guid bhid, - [MarshalAs(UnmanagedType.LPStruct)] Guid riid, - out nint ppv); - - void GetParent(out IShellItem ppsi); - - void GetDisplayName(SIGDN sigdnName, [MarshalAs(UnmanagedType.LPWStr)] out string ppszName); - - void GetAttributes(uint sfgaoMask, out uint psfgaoAttribs); - - void Compare(IShellItem psi, uint hint, out int piOrder); - } - - /// - /// see all STGM values - /// - [Flags] - public enum STGM : long - { - READ = 0x00000000L, - WRITE = 0x00000001L, - READWRITE = 0x00000002L, - CREATE = 0x00001000L, - } -} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Microsoft.CmdPal.Ext.WinGet.csproj b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Microsoft.CmdPal.Ext.WinGet.csproj index 7c5c6d2dd3..2f9da761d2 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Microsoft.CmdPal.Ext.WinGet.csproj +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Microsoft.CmdPal.Ext.WinGet.csproj @@ -32,6 +32,11 @@ + + + + + diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/NativeMethods.json b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/NativeMethods.json new file mode 100644 index 0000000000..02fff599f2 --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/NativeMethods.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/CsWin32.schema.json", + "allowMarshaling": false, + "comInterop": { + "preserveSigMethods": [ "*" ] + } +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/WindowsPackageManager.Interop/WindowsPackageManagerStandardFactory.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/WindowsPackageManager.Interop/WindowsPackageManagerStandardFactory.cs index d8b6064a86..677f688ac6 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/WindowsPackageManager.Interop/WindowsPackageManagerStandardFactory.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/WindowsPackageManager.Interop/WindowsPackageManagerStandardFactory.cs @@ -20,20 +20,25 @@ public class WindowsPackageManagerStandardFactory : WindowsPackageManagerFactory protected override T CreateInstance(Guid clsid, Guid iid) { var pUnknown = IntPtr.Zero; - try + unsafe { - var hr = PInvoke.CoCreateInstance(clsid, null, CLSCTX.CLSCTX_ALL, iid, out var result); - Marshal.ThrowExceptionForHR(hr); - pUnknown = Marshal.GetIUnknownForObject(result); - return MarshalGeneric.FromAbi(pUnknown); - } - finally - { - // CoCreateInstance and FromAbi both AddRef on the native object. - // Release once to prevent memory leak. - if (pUnknown != IntPtr.Zero) + try { - Marshal.Release(pUnknown); + var hr = PInvoke.CoCreateInstance(clsid, null, CLSCTX.CLSCTX_ALL, iid, out var result); + Marshal.ThrowExceptionForHR(hr); + + pUnknown = new IntPtr(result); + + return MarshalGeneric.FromAbi(pUnknown); + } + finally + { + // CoCreateInstance and FromAbi both AddRef on the native object. + // Release once to prevent memory leak. + if (pUnknown != IntPtr.Zero) + { + Marshal.Release(pUnknown); + } } } } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/IVirtualDesktopManager.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/IVirtualDesktopManager.cs index 070e24cc26..75170a56cc 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/IVirtualDesktopManager.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/IVirtualDesktopManager.cs @@ -4,6 +4,7 @@ using System; using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; namespace Microsoft.CmdPal.Ext.WindowWalker.Helpers; @@ -11,18 +12,16 @@ namespace Microsoft.CmdPal.Ext.WindowWalker.Helpers; /// Interface for accessing Virtual Desktop Manager. /// Code used from /// -[ComImport] -[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] +[GeneratedComInterface] [Guid("a5cd92ff-29be-454c-8d04-d82879fb3f1b")] -[System.Security.SuppressUnmanagedCodeSecurity] -internal interface IVirtualDesktopManager +public partial interface IVirtualDesktopManager { [PreserveSig] - int IsWindowOnCurrentVirtualDesktop([In] IntPtr hTopLevelWindow, [Out] out int onCurrentDesktop); + int IsWindowOnCurrentVirtualDesktop(IntPtr hTopLevelWindow, out int onCurrentDesktop); [PreserveSig] - int GetWindowDesktopId([In] IntPtr hTopLevelWindow, [Out] out Guid desktop); + int GetWindowDesktopId(IntPtr hTopLevelWindow, out Guid desktop); [PreserveSig] - int MoveWindowToDesktop([In] IntPtr hTopLevelWindow, [MarshalAs(UnmanagedType.LPStruct)][In] Guid desktop); + int MoveWindowToDesktop(IntPtr hTopLevelWindow, ref Guid desktop); } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/NativeMethods.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/NativeMethods.cs index e60cb262fe..21b2f7910c 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/NativeMethods.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/NativeMethods.cs @@ -13,7 +13,7 @@ using SuppressMessageAttribute = System.Diagnostics.CodeAnalysis.SuppressMessage namespace Microsoft.CmdPal.Ext.WindowWalker.Helpers; [SuppressMessage("Interoperability", "CA1401:P/Invokes should not be visible", Justification = "We want plugins to share this NativeMethods class, instead of each one creating its own.")] -public static class NativeMethods +public static partial class NativeMethods { [DllImport("user32.dll", CharSet = CharSet.Unicode)] public static extern int EnumWindows(EnumWindowsProc callPtr, IntPtr lParam); @@ -99,31 +99,14 @@ public static class NativeMethods [return: MarshalAs(UnmanagedType.Bool)] public static extern bool GetFirmwareType(ref FirmwareType FirmwareType); - [DllImport("user32.dll")] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool ExitWindowsEx(uint uFlags, uint dwReason); - - [DllImport("user32")] - public static extern void LockWorkStation(); - - [DllImport("Powrprof.dll", CharSet = CharSet.Auto, ExactSpelling = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool SetSuspendState(bool hibernate, bool forceCritical, bool disableWakeEvent); - - [DllImport("Shell32.dll", CharSet = CharSet.Unicode)] - public static extern uint SHEmptyRecycleBin(IntPtr hWnd, uint dwFlags); - - [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)] - public static extern HRESULT SHLoadIndirectString(string pszSource, StringBuilder pszOutBuf, uint cchOutBuf, IntPtr ppvReserved); - - [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)] - public static extern HRESULT SHCreateStreamOnFileEx(string fileName, STGM grfMode, uint attributes, bool create, System.Runtime.InteropServices.ComTypes.IStream reserved, out System.Runtime.InteropServices.ComTypes.IStream stream); - - [DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = true)] - public static extern int SHCreateItemFromParsingName([MarshalAs(UnmanagedType.LPWStr)] string path, IntPtr pbc, ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out IShellItem shellItem); - - [DllImport("rpcrt4.dll")] - public static extern int UuidCreateSequential(out GUIDDATA Uuid); + [LibraryImport("ole32.dll")] + [return: MarshalAs(UnmanagedType.U4)] + public static partial uint CoCreateInstance( + ref Guid rclsid, + IntPtr pUnkOuter, + uint dwClsContext, + ref Guid riid, + out IntPtr rReturnedComObject); } [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "These are the names used by win32.")] @@ -161,19 +144,6 @@ public static class Win32Constants public const int RPC_S_UUID_LOCAL_ONLY = 0x720; } -public static class ShellItemTypeConstants -{ - /// - /// Guid for type IShellItem. - /// - public static readonly Guid ShellItemGuid = new("43826d1e-e718-42ee-bc55-a1e261c37bfe"); - - /// - /// Guid for type IShellItem2. - /// - public static readonly Guid ShellItem2Guid = new("7E9FB0D3-919F-4307-AB2E-9B1860310C93"); -} - public enum HRESULT : uint { /// @@ -1124,26 +1094,6 @@ public enum ExtendedWindowStyles : uint WS_EX_NOACTIVATE = 0x8000000, } -[ComImport] -[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] -[Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe")] -public interface IShellItem -{ - void BindToHandler( - IntPtr pbc, - [MarshalAs(UnmanagedType.LPStruct)] Guid bhid, - [MarshalAs(UnmanagedType.LPStruct)] Guid riid, - out IntPtr ppv); - - void GetParent(out IShellItem ppsi); - - void GetDisplayName(SIGDN sigdnName, [MarshalAs(UnmanagedType.LPWStr)] out string ppszName); - - void GetAttributes(uint sfgaoMask, out uint psfgaoAttribs); - - void Compare(IShellItem psi, uint hint, out int piOrder); -} - /// /// The following are ShellItem DisplayName types. /// diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/VirtualDesktopHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/VirtualDesktopHelper.cs index 023bd8ed33..112e2361de 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/VirtualDesktopHelper.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/VirtualDesktopHelper.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; using System.Text; using Microsoft.CmdPal.Ext.WindowWalker.Properties; @@ -48,6 +49,10 @@ public class VirtualDesktopHelper /// private readonly List _availableDesktops = []; +#pragma warning disable SA1306 // Field names should begin with lower-case letter + private readonly uint CLSCTXINPROCALL = 0x17; +#pragma warning restore SA1306 // Field names should begin with lower-case letter + /// /// Id of the current visible Desktop. /// @@ -55,15 +60,27 @@ public class VirtualDesktopHelper private static readonly CompositeFormat VirtualDesktopHelperDesktop = System.Text.CompositeFormat.Parse(Properties.Resources.VirtualDesktopHelper_Desktop); + private Guid iVirtualDesktopManagerCLSID = new("aa509086-5ca9-4c25-8f95-589d3c07b48a"); + + private Guid iVirtualDesktopManagerIID = new("a5cd92ff-29be-454c-8d04-d82879fb3f1b"); + /// /// Initializes a new instance of the class. /// /// Setting to configure if the list of available desktops should update automatically or only when calling . Per default this is set to manual update (false) to have less registry queries. public VirtualDesktopHelper(bool desktopListUpdate = false) { + var cw = new StrategyBasedComWrappers(); + try { - _virtualDesktopManager = (IVirtualDesktopManager)new CVirtualDesktopManager(); + var hr = NativeMethods.CoCreateInstance(ref this.iVirtualDesktopManagerCLSID, nint.Zero, CLSCTXINPROCALL, ref iVirtualDesktopManagerIID, out var virtualDesktopManagerPtr); + if (hr != 0) + { + throw new ArgumentException($"Failed to create IVirtualDesktopManager instance. HR: 0x{hr:X}"); + } + + _virtualDesktopManager = (IVirtualDesktopManager)cw.GetOrCreateObjectForComInstance(virtualDesktopManagerPtr, CreateObjectFlags.None); } catch (COMException ex) { @@ -409,7 +426,7 @@ public class VirtualDesktopHelper /// Handle of the top level window. /// Guid of the target desktop. /// on success and on failure. - public bool MoveWindowToDesktop(IntPtr hWindow, in Guid desktopId) + public bool MoveWindowToDesktop(IntPtr hWindow, ref Guid desktopId) { if (_virtualDesktopManager == null) { @@ -417,7 +434,7 @@ public class VirtualDesktopHelper return false; } - var hr = _virtualDesktopManager.MoveWindowToDesktop(hWindow, desktopId); + var hr = _virtualDesktopManager.MoveWindowToDesktop(hWindow, ref desktopId); if (hr != (int)HRESULT.S_OK) { ExtensionHost.LogMessage(new LogMessage() { Message = "VirtualDesktopHelper.MoveWindowToDesktop() failed: An exception was thrown when moving the window ({hWindow}) to another desktop ({desktopId})." }); @@ -455,7 +472,7 @@ public class VirtualDesktopHelper } Guid newDesktop = _availableDesktops[windowDesktopNumber - 1]; - return MoveWindowToDesktop(hWindow, newDesktop); + return MoveWindowToDesktop(hWindow, ref newDesktop); } /// @@ -486,7 +503,7 @@ public class VirtualDesktopHelper } Guid newDesktop = _availableDesktops[windowDesktopNumber + 1]; - return MoveWindowToDesktop(hWindow, newDesktop); + return MoveWindowToDesktop(hWindow, ref newDesktop); } /// diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Commands/LaunchProfileAsAdminCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Commands/LaunchProfileAsAdminCommand.cs index 39155115ca..4a5170250a 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Commands/LaunchProfileAsAdminCommand.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Commands/LaunchProfileAsAdminCommand.cs @@ -3,18 +3,12 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Resources; -using System.Text; -using System.Threading.Tasks; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; using ManagedCommon; using Microsoft.CmdPal.Ext.WindowsTerminal.Helpers; using Microsoft.CmdPal.Ext.WindowsTerminal.Properties; -using Microsoft.CommandPalette.Extensions; using Microsoft.CommandPalette.Extensions.Toolkit; -using Windows.UI; namespace Microsoft.CmdPal.Ext.WindowsTerminal.Commands; @@ -68,7 +62,23 @@ internal sealed partial class LaunchProfileAsAdminCommand : InvokableCommand private void Launch(string id, string profile) { - var appManager = new ApplicationActivationManager(); + ComWrappers cw = new StrategyBasedComWrappers(); + + var hr = NativeHelpers.CoCreateInstance(ref NativeHelpers.ApplicationActivationManagerCLSID, IntPtr.Zero, NativeHelpers.CLSCTXINPROCALL, ref NativeHelpers.ApplicationActivationManagerIID, out var appManagerPtr); + + if (hr != 0) + { + throw new ArgumentException($"Failed to create IApplicationActivationManager instance. HR: 0x{hr:X}"); + } + + var appManager = (IApplicationActivationManager)cw.GetOrCreateObjectForComInstance( + appManagerPtr, CreateObjectFlags.None); + + if (appManager == null) + { + throw new ArgumentException("Failed to get IApplicationActivationManager interface"); + } + const ActivateOptions noFlags = ActivateOptions.None; var queryArguments = TerminalHelper.GetArguments(profile, _openNewTab, _openQuake); try diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Commands/LaunchProfileCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Commands/LaunchProfileCommand.cs index a879dc2410..5f9d4ea00d 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Commands/LaunchProfileCommand.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Commands/LaunchProfileCommand.cs @@ -3,18 +3,12 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Resources; -using System.Text; -using System.Threading.Tasks; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; using ManagedCommon; using Microsoft.CmdPal.Ext.WindowsTerminal.Helpers; using Microsoft.CmdPal.Ext.WindowsTerminal.Properties; -using Microsoft.CommandPalette.Extensions; using Microsoft.CommandPalette.Extensions.Toolkit; -using Windows.UI; namespace Microsoft.CmdPal.Ext.WindowsTerminal.Commands; @@ -38,7 +32,22 @@ internal sealed partial class LaunchProfileCommand : InvokableCommand private void Launch(string id, string profile) { - var appManager = new ApplicationActivationManager(); + ComWrappers cw = new StrategyBasedComWrappers(); + + var hr = NativeHelpers.CoCreateInstance(ref NativeHelpers.ApplicationActivationManagerCLSID, IntPtr.Zero, NativeHelpers.CLSCTXINPROCALL, ref NativeHelpers.ApplicationActivationManagerIID, out var appManagerPtr); + if (hr != 0) + { + throw new ArgumentException($"Failed to create IApplicationActivationManager instance. HR: 0x{hr:X}"); + } + + var appManager = (IApplicationActivationManager)cw.GetOrCreateObjectForComInstance( + appManagerPtr, CreateObjectFlags.None); + + if (appManager == null) + { + throw new ArgumentException("Failed to get IApplicationActivationManager interface"); + } + const ActivateOptions noFlags = ActivateOptions.None; var queryArguments = TerminalHelper.GetArguments(profile, _openNewTab, _openQuake); try diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/ApplicationActivationManager.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/ApplicationActivationManager.cs deleted file mode 100644 index a074d9c86d..0000000000 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/ApplicationActivationManager.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace Microsoft.CmdPal.Ext.WindowsTerminal.Helpers; - -// Application Activation Manager Class -[ComImport] -[Guid("45BA127D-10A8-46EA-8AB7-56EA9078943C")] -public class ApplicationActivationManager : IApplicationActivationManager -{ - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)/*, PreserveSig*/] - public extern IntPtr ActivateApplication([In] string appUserModelId, [In] string arguments, [In] ActivateOptions options, [Out] out uint processId); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public extern IntPtr ActivateForFile([In] string appUserModelId, [In] IntPtr /*IShellItemArray* */ itemArray, [In] string verb, [Out] out uint processId); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public extern IntPtr ActivateForProtocol([In] string appUserModelId, [In] IntPtr /* IShellItemArray* */itemArray, [Out] out uint processId); -} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/IApplicationActivationManager.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/IApplicationActivationManager.cs index e332eee6fd..c023323d7f 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/IApplicationActivationManager.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/IApplicationActivationManager.cs @@ -4,6 +4,7 @@ using System; using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; namespace Microsoft.CmdPal.Ext.WindowsTerminal.Helpers; @@ -18,14 +19,13 @@ public enum ActivateOptions } // ApplicationActivationManager -[ComImport] +[GeneratedComInterface(StringMarshalling = StringMarshalling.Utf16)] [Guid("2e941141-7f97-4756-ba1d-9decde894a3d")] -[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] -public interface IApplicationActivationManager +public partial interface IApplicationActivationManager { - IntPtr ActivateApplication([In] string appUserModelId, [In] string arguments, [In] ActivateOptions options, [Out] out uint processId); + void ActivateApplication(string appUserModelId, string arguments, ActivateOptions options, out uint processId); - IntPtr ActivateForFile([In] string appUserModelId, [In] IntPtr /*IShellItemArray* */ itemArray, [In] string verb, [Out] out uint processId); + void ActivateForFile(string appUserModelId, IntPtr /*IShellItemArray* */ itemArray, string verb, out uint processId); - IntPtr ActivateForProtocol([In] string appUserModelId, [In] IntPtr /* IShellItemArray* */itemArray, [Out] out uint processId); + void ActivateForProtocol(string appUserModelId, IntPtr /* IShellItemArray* */itemArray, out uint processId); } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/NativeHelpers.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/NativeHelpers.cs new file mode 100644 index 0000000000..e431d15a1b --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/NativeHelpers.cs @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.CmdPal.Ext.WindowsTerminal.Helpers; + +public sealed partial class NativeHelpers +{ + public const uint CLSCTXINPROCALL = 0x17; + + [LibraryImport("ole32.dll")] + public static partial uint CoCreateInstance( + ref Guid rclsid, + IntPtr pUnkOuter, + uint dwClsContext, + ref Guid riid, + out IntPtr rReturnedComObject); + +#pragma warning disable CA2211 // Non-constant fields should not be visible +#pragma warning disable SA1401 // Fields should be private + public static Guid ApplicationActivationManagerCLSID = new Guid("45BA127D-10A8-46EA-8AB7-56EA9078943C"); + public static Guid ApplicationActivationManagerIID = new Guid("2e941141-7f97-4756-ba1d-9decde894a3d"); +#pragma warning restore SA1401 // Fields should be private +#pragma warning restore CA2211 // Non-constant fields should not be visible +} From 78c7c8e88fc5d8bd177738d0e2c672c8a63f9c77 Mon Sep 17 00:00:00 2001 From: Noraa Junker-Wildi Date: Wed, 4 Jun 2025 11:09:41 +0200 Subject: [PATCH 034/117] Changing name from Aaron Junker to Noraa Junker (#39860) ## Summary of the Pull Request Changes my old name to my new name in community.md and the File Explorer add-ons attribution. ## PR Checklist - [ ] **Closes:** #xxx - [ ] **Communication:** I've discussed this with core contributors already. If work hasn't been agreed, this work might be rejected - [ ] **Tests:** Added/updated and all pass - [ ] **Localization:** All end user facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx ## Detailed Description of the Pull Request / Additional comments ## Validation Steps Performed --- .github/actions/spell-check/allow/names.txt | 2 ++ COMMUNITY.md | 4 ++-- .../Settings.UI/SettingsXAML/Views/PowerPreviewPage.xaml | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/actions/spell-check/allow/names.txt b/.github/actions/spell-check/allow/names.txt index eeda5b9ae1..f30d204c72 100644 --- a/.github/actions/spell-check/allow/names.txt +++ b/.github/actions/spell-check/allow/names.txt @@ -127,6 +127,8 @@ Naro nathancartlidge Nemeth nielslaute +Noraa +noraajunker oldnewthing onegreatworld palenshus diff --git a/COMMUNITY.md b/COMMUNITY.md index ccfa61decf..f6bfa8d67a 100644 --- a/COMMUNITY.md +++ b/COMMUNITY.md @@ -6,8 +6,8 @@ Names are in alphabetical order based on first name. ## High impact community members -### [@Aaron-Junker](https://github.com/Aaron-Junker) - [Aaron Junker](https://aaron-junker.github.io) -Aaron has helped triaging, discussing, and creating a substantial number of issues and contributed features/fixes. Aaron was the primary person for helping build the File Explorer preview pane handler for developer files. +### [@Noraa-Junker](https://github.com/Noraa-Junker) - [Noraa Junker](https://noraajunker.ch) +Noraa has helped triaging, discussing, and creating a substantial number of issues and contributed features/fixes. Noraa was the primary person for helping build the File Explorer preview pane handler for developer files. ### [@cgaarden](https://github.com/cgaarden) - [Christian Gaarden Gaardmark](https://www.onegreatworld.com) Christian contributed New+ utility diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerPreviewPage.xaml b/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerPreviewPage.xaml index 489a1a8b7d..301cf7c51e 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerPreviewPage.xaml +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerPreviewPage.xaml @@ -243,7 +243,7 @@ - + From ddbb6161e3fc9cea5d064ea6c64eabc69be3aa5a Mon Sep 17 00:00:00 2001 From: Yu Leng <42196638+moooyo@users.noreply.github.com> Date: Wed, 4 Jun 2025 17:15:55 +0800 Subject: [PATCH 035/117] [CmdPal][AOT] Make fileSearch ext become AOT compatible (#39732) ## Summary of the Pull Request * Use source generation to replace some csWin32 function call. * Clean up some AOT compatible issues. ## PR Checklist - [x] **Closes:** #39889 - [x] **Communication:** I've discussed this with core contributors already. If work hasn't been agreed, this work might be rejected - [x] **Tests:** Added/updated and all pass - [x] **Localization:** All end user facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx ## Detailed Description of the Pull Request / Additional comments ## Validation Steps Performed If you want to try it out, please see https://github.com/microsoft/PowerToys/pull/39605 --------- Co-authored-by: Yu Leng --- .github/actions/spell-check/expect.txt | 13 +- .../Commands/OpenPropertiesCommand.cs | 13 +- .../Commands/OpenWithCommand.cs | 14 +- .../Indexer/DataSourceManager.cs | 25 ++- .../Indexer/OleDB/DBPROP.cs | 4 +- .../Indexer/OleDB/IRowset.cs | 32 ++- .../Indexer/OleDB/IRowsetInfo.cs | 16 +- .../Indexer/SearchQuery.cs | 185 ++++-------------- .../Indexer/SearchResult.cs | 36 ++-- .../SystemSearch/CSearchCatalogManager.cs | 18 -- .../CSearchCatalogManagerClass.cs | 131 ------------- .../Indexer/SystemSearch/CSearchManager.cs | 18 -- .../SystemSearch/CSearchManagerClass.cs | 90 --------- .../SystemSearch/CSearchQueryHelper.cs | 18 -- .../SystemSearch/CSearchQueryHelperClass.cs | 134 ------------- .../Indexer/SystemSearch/ICommand.cs | 21 ++ .../Indexer/SystemSearch/ICommandText.cs | 23 +++ .../Indexer/SystemSearch/IDBCreateCommand.cs | 16 ++ .../Indexer/SystemSearch/IDBCreateSession.cs | 20 ++ .../Indexer/SystemSearch/IDBInitialize.cs | 22 +++ .../Indexer/SystemSearch/IGetRow.cs | 23 +++ .../Indexer/SystemSearch/IPropertyStore.cs | 26 +++ .../SystemSearch/ISearchCatalogManager.cs | 129 ++++-------- .../Indexer/SystemSearch/ISearchManager.cs | 85 +++----- .../SystemSearch/ISearchQueryHelper.cs | 162 +++++---------- .../Indexer/SystemSearch/PropVariant.cs | 70 +++++++ .../Indexer/SystemSearch/PropertyKey.cs | 27 +++ .../Indexer/Utils/QueryStringBuilder.cs | 45 ++++- .../Microsoft.CmdPal.Ext.Indexer.csproj | 11 +- .../Native/NativeHelpers.cs | 25 ++- .../Native/NativeMethods.cs | 47 +++++ .../NativeMethods.json | 6 + .../NativeMethods.txt | 11 +- 33 files changed, 603 insertions(+), 913 deletions(-) delete mode 100644 src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchCatalogManager.cs delete mode 100644 src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchCatalogManagerClass.cs delete mode 100644 src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchManager.cs delete mode 100644 src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchManagerClass.cs delete mode 100644 src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchQueryHelper.cs delete mode 100644 src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchQueryHelperClass.cs create mode 100644 src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ICommand.cs create mode 100644 src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ICommandText.cs create mode 100644 src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/IDBCreateCommand.cs create mode 100644 src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/IDBCreateSession.cs create mode 100644 src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/IDBInitialize.cs create mode 100644 src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/IGetRow.cs create mode 100644 src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/IPropertyStore.cs create mode 100644 src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/PropVariant.cs create mode 100644 src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/PropertyKey.cs create mode 100644 src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Native/NativeMethods.cs create mode 100644 src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/NativeMethods.json diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index ad79e2e8ca..6dd1d3577e 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -1995,4 +1995,15 @@ culori Evercoder LCh CIELCh -CLSCTXINPROCALL \ No newline at end of file +CLSCTXINPROCALL +IIDI +irow +lcid +OTHERUNZOOM +OTHERZOOM +PARENTCLOSING +PARENTOPENING +ppwsz +rguid +SCROLLCHILDREN +VARTYPE \ No newline at end of file diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/OpenPropertiesCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/OpenPropertiesCommand.cs index a6611cf6b3..cfd88184c6 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/OpenPropertiesCommand.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/OpenPropertiesCommand.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using ManagedCommon; using Microsoft.CmdPal.Ext.Indexer.Data; @@ -13,6 +14,7 @@ using Windows.Win32; using Windows.Win32.Foundation; using Windows.Win32.UI.Shell; using Windows.Win32.UI.WindowsAndMessaging; +using static Microsoft.CmdPal.Ext.Indexer.Native.NativeMethods; namespace Microsoft.CmdPal.Ext.Indexer.Commands; @@ -27,19 +29,16 @@ internal sealed partial class OpenPropertiesCommand : InvokableCommand try { - var filenamePCWSTR = new PCWSTR((char*)filenamePtr); - var propertiesPCWSTR = new PCWSTR((char*)propertiesPtr); - var info = new SHELLEXECUTEINFOW { - cbSize = (uint)Marshal.SizeOf(), - lpVerb = propertiesPCWSTR, - lpFile = filenamePCWSTR, + cbSize = (uint)sizeof(SHELLEXECUTEINFOW), + lpVerb = propertiesPtr, + lpFile = filenamePtr, nShow = (int)SHOW_WINDOW_CMD.SW_SHOW, fMask = NativeHelpers.SEEMASKINVOKEIDLIST, }; - return PInvoke.ShellExecuteEx(ref info); + return ShellExecuteEx(ref info); } finally { diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/OpenWithCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/OpenWithCommand.cs index a9c431d32c..48aa175f52 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/OpenWithCommand.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/OpenWithCommand.cs @@ -2,6 +2,8 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Microsoft.CmdPal.Ext.Indexer.Data; using Microsoft.CmdPal.Ext.Indexer.Native; @@ -11,6 +13,7 @@ using Windows.Win32; using Windows.Win32.Foundation; using Windows.Win32.UI.Shell; using Windows.Win32.UI.WindowsAndMessaging; +using static Microsoft.CmdPal.Ext.Indexer.Native.NativeMethods; namespace Microsoft.CmdPal.Ext.Indexer.Commands; @@ -25,19 +28,16 @@ internal sealed partial class OpenWithCommand : InvokableCommand try { - var filenamePCWSTR = new PCWSTR((char*)filenamePtr); - var verbPCWSTR = new PCWSTR((char*)verbPtr); - var info = new SHELLEXECUTEINFOW { - cbSize = (uint)Marshal.SizeOf(), - lpVerb = verbPCWSTR, - lpFile = filenamePCWSTR, + cbSize = (uint)sizeof(SHELLEXECUTEINFOW), + lpVerb = verbPtr, + lpFile = filenamePtr, nShow = (int)SHOW_WINDOW_CMD.SW_SHOWNORMAL, fMask = NativeHelpers.SEEMASKINVOKEIDLIST, }; - return PInvoke.ShellExecuteEx(ref info); + return ShellExecuteEx(ref info); } finally { diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/DataSourceManager.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/DataSourceManager.cs index e186251825..01c6d84357 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/DataSourceManager.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/DataSourceManager.cs @@ -3,17 +3,17 @@ // See the LICENSE file in the project root for more information. using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; using ManagedCommon; -using Windows.Win32; -using Windows.Win32.System.Com; -using Windows.Win32.System.Search; +using Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch; +using Microsoft.CmdPal.Ext.Indexer.Native; namespace Microsoft.CmdPal.Ext.Indexer.Indexer; internal static class DataSourceManager { - private static readonly Guid CLSIDCollatorDataSource = new("9E175B8B-F52A-11D8-B9A5-505054503030"); - private static IDBInitialize _dataSource; public static IDBInitialize GetDataSource() @@ -28,20 +28,29 @@ internal static class DataSourceManager private static bool InitializeDataSource() { - var hr = PInvoke.CoCreateInstance(CLSIDCollatorDataSource, null, CLSCTX.CLSCTX_INPROC_SERVER, typeof(IDBInitialize).GUID, out var dataSourceObj); + var riid = typeof(IDBInitialize).GUID; + var hr = NativeMethods.CoCreateInstance(ref Unsafe.AsRef(in NativeHelpers.CsWin32GUID.CLSIDCollatorDataSource), IntPtr.Zero, NativeHelpers.CLSCTXINPROCALL, ref riid, out var dataSourceObjPtr); if (hr != 0) { Logger.LogError("CoCreateInstance failed: " + hr); return false; } - if (dataSourceObj == null) + if (dataSourceObjPtr == IntPtr.Zero) + { + Logger.LogError("CoCreateInstance failed: dataSourceObjPtr is null"); + return false; + } + + var comWrappers = new StrategyBasedComWrappers(); + _dataSource = (IDBInitialize)comWrappers.GetOrCreateObjectForComInstance(dataSourceObjPtr, CreateObjectFlags.None); + + if (_dataSource == null) { Logger.LogError("CoCreateInstance failed: dataSourceObj is null"); return false; } - _dataSource = (IDBInitialize)dataSourceObj; _dataSource.Initialize(); return true; diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/DBPROP.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/DBPROP.cs index d4be1b967f..cf4c090ca8 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/DBPROP.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/DBPROP.cs @@ -3,6 +3,8 @@ // See the LICENSE file in the project root for more information. using System.Runtime.InteropServices; +using Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch; +using Microsoft.CmdPal.Ext.Indexer.Native; using Windows.Win32.Storage.IndexServer; using Windows.Win32.System.Com.StructuredStorage; @@ -16,6 +18,6 @@ internal struct DBPROP public uint dwOptions; public uint dwStatus; public DBID colid; - public PROPVARIANT vValue; + public PropVariant vValue; #pragma warning restore SA1307 // Accessible fields should begin with upper-case letter } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/IRowset.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/IRowset.cs index 3127e2e563..75502f56c2 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/IRowset.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/IRowset.cs @@ -4,40 +4,38 @@ using System; using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; namespace Microsoft.CmdPal.Ext.Indexer.Indexer.OleDB; -[ComImport] [Guid("0c733a7c-2a1c-11ce-ade5-00aa0044773d")] -[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] -public interface IRowset +[GeneratedComInterface] +public partial interface IRowset { - [PreserveSig] - int AddRefRows( + void AddRefRows( uint cRows, - [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] IntPtr[] rghRows, - [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] uint[] rgRefCounts, - [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] int[] rgRowStatus); + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] IntPtr[] rghRows, + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] uint[] rgRefCounts, + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] int[] rgRowStatus); - [PreserveSig] - int GetData( + void GetData( IntPtr hRow, IntPtr hAccessor, IntPtr pData); - [PreserveSig] - int GetNextRows( + void GetNextRows( IntPtr hReserved, long lRowsOffset, long cRows, out uint pcRowsObtained, out IntPtr prghRows); - [PreserveSig] - int ReleaseRows( + void ReleaseRows( uint cRows, - [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] IntPtr[] rghRows, + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] IntPtr[] rghRows, IntPtr rgRowOptions, - [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] uint[] rgRefCounts, - [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] int[] rgRowStatus); + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] uint[] rgRefCounts, + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] int[] rgRowStatus); + + void RestartPosition(nuint hReserved); } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/IRowsetInfo.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/IRowsetInfo.cs index 5c891c8036..bf1406179b 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/IRowsetInfo.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/IRowsetInfo.cs @@ -4,29 +4,29 @@ using System; using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; namespace Microsoft.CmdPal.Ext.Indexer.Indexer.OleDB; -[ComImport] [Guid("0C733A55-2A1C-11CE-ADE5-00AA0044773D")] -[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] -public interface IRowsetInfo +[GeneratedComInterface] +public partial interface IRowsetInfo { [PreserveSig] int GetProperties( uint cPropertyIDSets, - [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] DBPROPIDSET[] rgPropertyIDSets, + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] DBPROPIDSET[] rgPropertyIDSets, out ulong pcPropertySets, out IntPtr prgPropertySets); [PreserveSig] int GetReferencedRowset( uint iOrdinal, - [In] ref Guid riid, - [Out, MarshalAs(UnmanagedType.Interface)] out object ppReferencedRowset); + ref Guid riid, + [MarshalAs(UnmanagedType.Interface)] out object ppReferencedRowset); [PreserveSig] int GetSpecification( - [In] ref Guid riid, - [Out, MarshalAs(UnmanagedType.Interface)] out object ppSpecification); + ref Guid riid, + [MarshalAs(UnmanagedType.Interface)] out object ppSpecification); } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SearchQuery.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SearchQuery.cs index abe12396ea..9faa853eb3 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SearchQuery.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SearchQuery.cs @@ -8,12 +8,10 @@ using System.Runtime.InteropServices; using System.Threading; using ManagedCommon; using Microsoft.CmdPal.Ext.Indexer.Indexer.OleDB; +using Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch; using Microsoft.CmdPal.Ext.Indexer.Indexer.Utils; using Microsoft.CmdPal.Ext.Indexer.Native; -using Windows.Win32; -using Windows.Win32.System.Com; -using Windows.Win32.System.Search; -using Windows.Win32.UI.Shell.PropertiesSystem; +using static Microsoft.CmdPal.Ext.Indexer.Native.NativeHelpers; namespace Microsoft.CmdPal.Ext.Indexer.Indexer; @@ -120,11 +118,6 @@ internal sealed partial class SearchQuery : IDisposable // We need to generate a search query string with the search text the user entered above if (currentRowset != null) { - if (reuseRowset != null) - { - Marshal.ReleaseComObject(reuseRowset); - } - // We have a previous rowset, this means the user is typing and we should store this // recapture the where ID from this so the next ExecuteSync call will be faster reuseRowset = currentRowset; @@ -148,13 +141,12 @@ internal sealed partial class SearchQuery : IDisposable private bool HandleRow(IGetRow getRow, nuint rowHandle) { - object propertyStorePtr = null; - try { - getRow.GetRowFromHROW(null, rowHandle, typeof(IPropertyStore).GUID, out propertyStorePtr); + var riid = CsWin32GUID.PropertyStore; + + getRow.GetRowFromHROW(null, rowHandle, ref riid, out var propertyStore); - var propertyStore = (IPropertyStore)propertyStorePtr; if (propertyStore == null) { Logger.LogError("Failed to get IPropertyStore interface"); @@ -176,14 +168,6 @@ internal sealed partial class SearchQuery : IDisposable Logger.LogError("Error handling row", ex); return false; } - finally - { - // Ensure the COM object is released if not returned - if (propertyStorePtr != null) - { - Marshal.ReleaseComObject(propertyStorePtr); - } - } } public bool FetchRows(int offset, int limit) @@ -194,16 +178,17 @@ internal sealed partial class SearchQuery : IDisposable return false; } - if (currentRowset is not IGetRow) + IGetRow getRow = null; + + try + { + getRow = (IGetRow)currentRowset; + } + catch (Exception) { Logger.LogInfo("Reset the current rowset"); ExecuteSyncInternal(); - } - - if (currentRowset is not IGetRow getRow) - { - Logger.LogError("Rowset does not support IGetRow interface"); - return false; + getRow = (IGetRow)currentRowset; } uint rowCountReturned; @@ -211,12 +196,7 @@ internal sealed partial class SearchQuery : IDisposable try { - var res = currentRowset.GetNextRows(IntPtr.Zero, offset, limit, out rowCountReturned, out prghRows); - if (res < 0) - { - Logger.LogError($"Error fetching rows: {res}"); - return false; - } + currentRowset.GetNextRows(IntPtr.Zero, offset, limit, out rowCountReturned, out prghRows); if (rowCountReturned == 0) { @@ -237,11 +217,7 @@ internal sealed partial class SearchQuery : IDisposable } } - res = currentRowset.ReleaseRows(rowCountReturned, rowHandles, IntPtr.Zero, null, null); - if (res != 0) - { - Logger.LogError($"Error releasing rows: {res}"); - } + currentRowset.ReleaseRows(rowCountReturned, rowHandles, IntPtr.Zero, null, null); Marshal.FreeCoTaskMem(prghRows); prghRows = IntPtr.Zero; @@ -268,11 +244,6 @@ internal sealed partial class SearchQuery : IDisposable var rowset = ExecuteCommand(queryStr); if (rowset != null) { - if (reuseRowset != null) - { - Marshal.ReleaseComObject(reuseRowset); - } - reuseRowset = rowset; reuseWhereID = GetReuseWhereId(reuseRowset); } @@ -280,110 +251,50 @@ internal sealed partial class SearchQuery : IDisposable private unsafe IRowset ExecuteCommand(string queryStr) { - object sessionPtr = null; - object commandPtr = null; + if (string.IsNullOrEmpty(queryStr)) + { + return null; + } try { var session = (IDBCreateSession)DataSourceManager.GetDataSource(); - session.CreateSession(null, typeof(IDBCreateCommand).GUID, out sessionPtr); - if (sessionPtr == null) + var guid = typeof(IDBCreateCommand).GUID; + session.CreateSession(IntPtr.Zero, ref guid, out var ppDBSession); + + if (ppDBSession == null) { Logger.LogError("CreateSession failed"); return null; } - var createCommand = (IDBCreateCommand)sessionPtr; - createCommand.CreateCommand(null, typeof(ICommandText).GUID, out commandPtr); - if (commandPtr == null) - { - Logger.LogError("CreateCommand failed"); - return null; - } + var createCommand = (IDBCreateCommand)ppDBSession; + guid = typeof(ICommandText).GUID; + createCommand.CreateCommand(IntPtr.Zero, ref guid, out ICommandText commandText); - var commandText = (ICommandText)commandPtr; if (commandText == null) { Logger.LogError("Failed to get ICommandText interface"); return null; } - commandText.SetCommandText(in NativeHelpers.OleDb.DbGuidDefault, queryStr); - commandText.Execute(null, typeof(IRowset).GUID, null, null, out var rowsetPointer); + var riid = NativeHelpers.OleDb.DbGuidDefault; - return rowsetPointer as IRowset; + var irowSetRiid = typeof(IRowset).GUID; + + commandText.SetCommandText(ref riid, queryStr); + commandText.Execute(null, ref irowSetRiid, null, out var pcRowsAffected, out var rowsetPointer); + + return rowsetPointer; } catch (Exception ex) { Logger.LogError("Unexpected error.", ex); return null; } - finally - { - // Release the command pointer - if (commandPtr != null) - { - Marshal.ReleaseComObject(commandPtr); - } - - // Release the session pointer - if (sessionPtr != null) - { - Marshal.ReleaseComObject(sessionPtr); - } - } } - private IRowsetInfo GetRowsetInfo(IRowset rowset) - { - if (rowset == null) - { - return null; - } - - var rowsetPtr = IntPtr.Zero; - var rowsetInfoPtr = IntPtr.Zero; - - try - { - // Get the IUnknown pointer for the IRowset object - rowsetPtr = Marshal.GetIUnknownForObject(rowset); - - // Query for IRowsetInfo interface - var rowsetInfoGuid = typeof(IRowsetInfo).GUID; - var res = Marshal.QueryInterface(rowsetPtr, in rowsetInfoGuid, out rowsetInfoPtr); - if (res != 0) - { - Logger.LogError($"Error getting IRowsetInfo interface: {res}"); - return null; - } - - // Marshal the interface pointer to the actual IRowsetInfo object - var rowsetInfo = (IRowsetInfo)Marshal.GetObjectForIUnknown(rowsetInfoPtr); - return rowsetInfo; - } - catch (Exception ex) - { - Logger.LogError($"Exception occurred while getting IRowsetInfo. ", ex); - return null; - } - finally - { - // Release the IRowsetInfo pointer if it was obtained - if (rowsetInfoPtr != IntPtr.Zero) - { - Marshal.Release(rowsetInfoPtr); // Release the IRowsetInfo pointer - } - - // Release the IUnknown pointer for the IRowset object - if (rowsetPtr != IntPtr.Zero) - { - Marshal.Release(rowsetPtr); - } - } - } - - private DBPROP? GetPropset(IRowsetInfo rowsetInfo) + private unsafe DBPROP? GetPropset(IRowsetInfo rowsetInfo) { var prgPropSetsPtr = IntPtr.Zero; @@ -403,16 +314,15 @@ internal sealed partial class SearchQuery : IDisposable return null; } - var firstPropSetPtr = new IntPtr(prgPropSetsPtr.ToInt64()); - var propSet = Marshal.PtrToStructure(firstPropSetPtr); + var firstPropSetPtr = (DBPROPSET*)prgPropSetsPtr.ToInt64(); + var propSet = *firstPropSetPtr; if (propSet.cProperties == 0 || propSet.rgProperties == IntPtr.Zero) { return null; } - var propPtr = new IntPtr(propSet.rgProperties.ToInt64()); - var prop = Marshal.PtrToStructure(propPtr); - return prop; + var propPtr = (DBPROP*)propSet.rgProperties.ToInt64(); + return *propPtr; } catch (Exception ex) { @@ -431,7 +341,8 @@ internal sealed partial class SearchQuery : IDisposable private uint GetReuseWhereId(IRowset rowset) { - var rowsetInfo = GetRowsetInfo(rowset); + var rowsetInfo = (IRowsetInfo)rowset; + if (rowsetInfo == null) { return 0; @@ -443,9 +354,9 @@ internal sealed partial class SearchQuery : IDisposable return 0; } - if (prop?.vValue.Anonymous.Anonymous.vt == VARENUM.VT_UI4) + if (prop?.vValue.VarType == VarEnum.VT_UI4) { - var value = prop?.vValue.Anonymous.Anonymous.Anonymous.ulVal; + var value = prop?.vValue._ulong; return (uint)value; } @@ -462,18 +373,6 @@ internal sealed partial class SearchQuery : IDisposable Marshal.FreeCoTaskMem(dbPropIdSet.rgPropertyIDs); } - if (reuseRowset != null) - { - Marshal.ReleaseComObject(reuseRowset); - reuseRowset = null; - } - - if (currentRowset != null) - { - Marshal.ReleaseComObject(currentRowset); - currentRowset = null; - } - queryCompletedEvent?.Dispose(); } } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SearchResult.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SearchResult.cs index 1840338b73..7c6cbdf3f1 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SearchResult.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SearchResult.cs @@ -3,12 +3,11 @@ // See the LICENSE file in the project root for more information. using System; +using System.Runtime.InteropServices; using ManagedCommon; +using Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch; using Microsoft.CmdPal.Ext.Indexer.Indexer.Utils; using Microsoft.CmdPal.Ext.Indexer.Native; -using Windows.Win32.System.Com; -using Windows.Win32.System.Com.StructuredStorage; -using Windows.Win32.UI.Shell.PropertiesSystem; namespace Microsoft.CmdPal.Ext.Indexer.Indexer; @@ -39,14 +38,9 @@ internal sealed class SearchResult { try { - var key = NativeHelpers.PropertyKeys.PKEYItemNameDisplay; - propStore.GetValue(&key, out var itemNameDisplay); - - key = NativeHelpers.PropertyKeys.PKEYItemUrl; - propStore.GetValue(&key, out var itemUrl); - - key = NativeHelpers.PropertyKeys.PKEYKindText; - propStore.GetValue(&key, out var kindText); + propStore.GetValue(NativeHelpers.PropertyKeys.PKEYItemNameDisplay, out var itemNameDisplay); + propStore.GetValue(NativeHelpers.PropertyKeys.PKEYItemUrl, out var itemUrl); + propStore.GetValue(NativeHelpers.PropertyKeys.PKEYKindText, out var kindText); var filePath = GetFilePath(ref itemUrl); var isFolder = IsFoder(ref kindText); @@ -67,28 +61,34 @@ internal sealed class SearchResult } } - private static bool IsFoder(ref PROPVARIANT kindText) + private static bool IsFoder(ref PropVariant kindText) { var kindString = GetStringFromPropVariant(ref kindText); return string.Equals(kindString, "Folder", StringComparison.OrdinalIgnoreCase); } - private static string GetFilePath(ref PROPVARIANT itemUrl) + private static string GetFilePath(ref PropVariant itemUrl) { var filePath = GetStringFromPropVariant(ref itemUrl); filePath = UrlToFilePathConverter.Convert(filePath); return filePath; } - private static string GetStringFromPropVariant(ref PROPVARIANT propVariant) + private static string GetStringFromPropVariant(ref PropVariant propVariant) { - if (propVariant.Anonymous.Anonymous.vt == VARENUM.VT_LPWSTR) + if (propVariant.VarType == System.Runtime.InteropServices.VarEnum.VT_LPWSTR) { - var pwszVal = propVariant.Anonymous.Anonymous.Anonymous.pwszVal; - if (pwszVal != null) + var pwszVal = propVariant._ptr; + + if (pwszVal == IntPtr.Zero) { - return pwszVal.ToString(); + return string.Empty; } + + // convert to string + var str = Marshal.PtrToStringUni(pwszVal); + + return str; } return string.Empty; diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchCatalogManager.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchCatalogManager.cs deleted file mode 100644 index bc2b855844..0000000000 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchCatalogManager.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Runtime.InteropServices; - -namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch; - -[CoClass(typeof(CSearchCatalogManagerClass))] -[Guid("AB310581-AC80-11D1-8DF3-00C04FB6EF50")] -[ComImport] -[System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1715:Identifiers should have correct prefix", Justification = "Using original name from type lib")] -[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1302:Interface names should begin with I", Justification = "Using original name from type lib")] -[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Using original name from type lib")] -public interface CSearchCatalogManager : ISearchCatalogManager -{ -} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchCatalogManagerClass.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchCatalogManagerClass.cs deleted file mode 100644 index 6f64e518a0..0000000000 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchCatalogManagerClass.cs +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch; - -[ComConversionLoss] -[Guid("AAB49DD5-AD0B-40AE-B654-AE8976BF6BD2")] -[ClassInterface((short)0)] -[ComImport] -[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1212:Property accessors should follow order", Justification = "The order of the property accessors must match the order in which the methods were defined in the vtable")] -public class CSearchCatalogManagerClass : ISearchCatalogManager, CSearchCatalogManager -{ - [DispId(1610678272)] - public virtual extern string Name - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.LPWStr)] - get; - } - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public virtual extern IntPtr GetParameter([MarshalAs(UnmanagedType.LPWStr), In] string pszName); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public virtual extern void SetParameter([MarshalAs(UnmanagedType.LPWStr), In] string pszName, [In] ref object pValue); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public virtual extern void GetCatalogStatus( - out object pStatus, - out object pPausedReason); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public virtual extern void Reset(); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public virtual extern void Reindex(); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public virtual extern void ReindexMatchingURLs([MarshalAs(UnmanagedType.LPWStr), In] string pszPattern); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public virtual extern void ReindexSearchRoot([MarshalAs(UnmanagedType.LPWStr), In] string pszRoot); - - [DispId(1610678280)] - public virtual extern uint ConnectTimeout - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } - - [DispId(1610678282)] - public virtual extern uint DataTimeout - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public virtual extern int NumberOfItems(); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public virtual extern void NumberOfItemsToIndex( - out int plIncrementalCount, - out int plNotificationQueue, - out int plHighPriorityQueue); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.LPWStr)] - public virtual extern string URLBeingIndexed(); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public virtual extern uint GetURLIndexingState([MarshalAs(UnmanagedType.LPWStr), In] string psz); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.Interface)] - public virtual extern object GetPersistentItemsChangedSink(); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public virtual extern void RegisterViewForNotification( - [MarshalAs(UnmanagedType.LPWStr), In] string pszView, - [MarshalAs(UnmanagedType.Interface), In] object pViewChangedSink, - out uint pdwCookie); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public virtual extern void GetItemsChangedSink( - [MarshalAs(UnmanagedType.Interface), In] object pISearchNotifyInlineSite, - [In] ref Guid riid, - out IntPtr ppv, - out Guid pGUIDCatalogResetSignature, - out Guid pGUIDCheckPointSignature, - out uint pdwLastCheckPointNumber); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public virtual extern void UnregisterViewForNotification([In] uint dwCookie); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public virtual extern void SetExtensionClusion([MarshalAs(UnmanagedType.LPWStr), In] string pszExtension, [In] int fExclude); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.Interface)] - public virtual extern object EnumerateExcludedExtensions(); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.Interface)] - public virtual extern CSearchQueryHelper GetQueryHelper(); - - [DispId(1610678295)] - public virtual extern int DiacriticSensitivity - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.Interface)] - public virtual extern object GetCrawlScopeManager(); -} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchManager.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchManager.cs deleted file mode 100644 index 210bb3437e..0000000000 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchManager.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Runtime.InteropServices; - -namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch; - -[CoClass(typeof(CSearchManagerClass))] -[Guid("AB310581-AC80-11D1-8DF3-00C04FB6EF69")] -[ComImport] -[System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1715:Identifiers should have correct prefix", Justification = "Using original name from type lib")] -[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1302:Interface names should begin with I", Justification = "Using original name from type lib")] -[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Using original name from type lib")] -public interface CSearchManager : ISearchManager -{ -} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchManagerClass.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchManagerClass.cs deleted file mode 100644 index 19a8067bb7..0000000000 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchManagerClass.cs +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch; - -[Guid("7D096C5F-AC08-4F1F-BEB7-5C22C517CE39")] -[TypeLibType(2)] -[ClassInterface((short)0)] -[ComConversionLoss] -[ComImport] -public class CSearchManagerClass : ISearchManager, CSearchManager -{ - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public virtual extern void GetIndexerVersionStr([MarshalAs(UnmanagedType.LPWStr)] out string ppszVersionString); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public virtual extern void GetIndexerVersion(out uint pdwMajor, out uint pdwMinor); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public virtual extern IntPtr GetParameter([MarshalAs(UnmanagedType.LPWStr), In] string pszName); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public virtual extern void SetParameter([MarshalAs(UnmanagedType.LPWStr), In] string pszName, [In] ref object pValue); - - [DispId(1610678276)] - public virtual extern string ProxyName - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.LPWStr)] - get; - } - - [DispId(1610678277)] - public virtual extern string BypassList - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.LPWStr)] - get; - } - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public virtual extern void SetProxy( - [In] object sUseProxy, - [In] int fLocalByPassProxy, - [In] uint dwPortNumber, - [MarshalAs(UnmanagedType.LPWStr), In] string pszProxyName, - [MarshalAs(UnmanagedType.LPWStr), In] string pszByPassList); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.Interface)] - public virtual extern CSearchCatalogManager GetCatalog([MarshalAs(UnmanagedType.LPWStr), In] string pszCatalog); - - [DispId(1610678280)] - public virtual extern string UserAgent - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.LPWStr)] - get; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: MarshalAs(UnmanagedType.LPWStr)] - [param: In] - set; - } - - [DispId(1610678282)] - public virtual extern object UseProxy - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } - - [DispId(1610678283)] - public virtual extern int LocalBypass - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } - - [DispId(1610678284)] - public virtual extern uint PortNumber - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } -} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchQueryHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchQueryHelper.cs deleted file mode 100644 index c4a08859ff..0000000000 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchQueryHelper.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Runtime.InteropServices; - -namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch; - -[Guid("AB310581-AC80-11D1-8DF3-00C04FB6EF63")] -[CoClass(typeof(CSearchQueryHelperClass))] -[ComImport] -[System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1715:Identifiers should have correct prefix", Justification = "Using original name from type lib")] -[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1302:Interface names should begin with I", Justification = "Using original name from type lib")] -[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Using original name from type lib")] -public interface CSearchQueryHelper : ISearchQueryHelper -{ -} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchQueryHelperClass.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchQueryHelperClass.cs deleted file mode 100644 index f89de6050b..0000000000 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchQueryHelperClass.cs +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch; - -[ClassInterface((short)0)] -[Guid("B271E955-09E1-42E1-9B95-5994A534B613")] -[ComImport] -[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1212:Property accessors should follow order", Justification = "The order of the property accessors must match the order in which the methods were defined in the vtable")] -public class CSearchQueryHelperClass : ISearchQueryHelper, CSearchQueryHelper -{ - [DispId(1610678272)] - public virtual extern string ConnectionString - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.LPWStr)] - get; - } - - [DispId(1610678273)] - public virtual extern uint QueryContentLocale - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } - - [DispId(1610678275)] - public virtual extern uint QueryKeywordLocale - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } - - [DispId(1610678277)] - public virtual extern object QueryTermExpansion - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } - - [DispId(1610678279)] - public virtual extern object QuerySyntax - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } - - [DispId(1610678281)] - public virtual extern string QueryContentProperties - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: MarshalAs(UnmanagedType.LPWStr)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.LPWStr)] - get; - } - - [DispId(1610678283)] - public virtual extern string QuerySelectColumns - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: MarshalAs(UnmanagedType.LPWStr)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.LPWStr)] - get; - } - - [DispId(1610678285)] - public virtual extern string QueryWhereRestrictions - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: MarshalAs(UnmanagedType.LPWStr)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.LPWStr)] - get; - } - - [DispId(1610678287)] - public virtual extern string QuerySorting - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: MarshalAs(UnmanagedType.LPWStr)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.LPWStr)] - get; - } - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.LPWStr)] - public virtual extern string GenerateSQLFromUserQuery([MarshalAs(UnmanagedType.LPWStr), In] string pszQuery); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public virtual extern void WriteProperties( - [In] int itemID, - [In] uint dwNumberOfColumns, - [In] ref object pColumns, - [In] ref object pValues, - [In] ref object pftGatherModifiedTime); - - [DispId(1610678291)] - public virtual extern int QueryMaxResults - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } -} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ICommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ICommand.cs new file mode 100644 index 0000000000..e04da52d1c --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ICommand.cs @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; +using Microsoft.CmdPal.Ext.Indexer.Indexer.OleDB; + +namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch; + +[Guid("0C733A63-2A1C-11CE-ADE5-00AA0044773D")] +[GeneratedComInterface(StringMarshalling = StringMarshalling.Utf16)] +public partial interface ICommand +{ + void Cancel(); + + void Execute([MarshalAs(UnmanagedType.Interface)] object pUnkOuter, ref Guid riid, [Optional][MarshalAs(UnmanagedType.Interface)] object pParams, [Optional]out int pcRowsAffected, out IRowset ppRowset); + + void GetDBSession(ref Guid riid, out IntPtr ppSession); +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ICommandText.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ICommandText.cs new file mode 100644 index 0000000000..f01aa48810 --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ICommandText.cs @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; +using System.Text; +using System.Threading.Tasks; +using Microsoft.CmdPal.Ext.Indexer.Indexer.OleDB; + +namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch; + +[Guid("0C733A27-2A1C-11CE-ADE5-00AA0044773D")] +[GeneratedComInterface(StringMarshalling = StringMarshalling.Utf16)] +public partial interface ICommandText : ICommand +{ + void GetCommandText([Optional] ref Guid pguidDialect, out IntPtr ppwszCommand); + + void SetCommandText(ref Guid rguidDialect, string pwszCommand); +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/IDBCreateCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/IDBCreateCommand.cs new file mode 100644 index 0000000000..25bd1c1c4e --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/IDBCreateCommand.cs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch; + +[Guid("0C733A1D-2A1C-11CE-ADE5-00AA0044773D")] +[GeneratedComInterface(StringMarshalling = StringMarshalling.Utf16)] +public partial interface IDBCreateCommand +{ + void CreateCommand(IntPtr pUnkOuter, ref Guid riid, out ICommandText ppCommand); +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/IDBCreateSession.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/IDBCreateSession.cs new file mode 100644 index 0000000000..6d2f0cafef --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/IDBCreateSession.cs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch; + +[Guid("0C733A5D-2A1C-11CE-ADE5-00AA0044773D")] +[GeneratedComInterface(StringMarshalling = StringMarshalling.Utf16)] +public partial interface IDBCreateSession +{ + void CreateSession(IntPtr pUnkOuter, ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out object ppDBSession); +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/IDBInitialize.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/IDBInitialize.cs new file mode 100644 index 0000000000..40f8532427 --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/IDBInitialize.cs @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch; + +[Guid("0C733A8B-2A1C-11CE-ADE5-00AA0044773D")] +[GeneratedComInterface(StringMarshalling = StringMarshalling.Utf16)] +public partial interface IDBInitialize +{ + void Initialize(); + + void Uninitialize(); +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/IGetRow.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/IGetRow.cs new file mode 100644 index 0000000000..5f6635216b --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/IGetRow.cs @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; +using System.Text; +using System.Threading.Tasks; +using Microsoft.CmdPal.Ext.Indexer.Native; + +namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch; + +[Guid("0C733AAF-2A1C-11CE-ADE5-00AA0044773D")] +[GeneratedComInterface(StringMarshalling = StringMarshalling.Utf16)] +public partial interface IGetRow +{ + unsafe void GetRowFromHROW([MarshalAs(UnmanagedType.Interface)] object pUnkOuter, nuint hRow, ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out IPropertyStore ppUnk); + + unsafe string GetURLFromHROW(nuint hRow); +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/IPropertyStore.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/IPropertyStore.cs new file mode 100644 index 0000000000..1bd733f7b9 --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/IPropertyStore.cs @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +[assembly: System.Runtime.CompilerServices.DisableRuntimeMarshalling] + +namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch; + +[Guid("886d8eeb-8cf2-4446-8d02-cdba1dbdcf99")] +[GeneratedComInterface(StringMarshalling = StringMarshalling.Utf16)] +public partial interface IPropertyStore +{ + uint GetCount(); + + PropertyKey GetAt(uint iProp); + + void GetValue(in PropertyKey pkey, out PropVariant pv); + + void SetValue(in PropertyKey pkey, in PropVariant pv); + + void Commit(); +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ISearchCatalogManager.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ISearchCatalogManager.cs index 04f6f76be0..f89c882669 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ISearchCatalogManager.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ISearchCatalogManager.cs @@ -5,125 +5,68 @@ using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch; [Guid("AB310581-AC80-11D1-8DF3-00C04FB6EF50")] -[ComConversionLoss] -[InterfaceType(1)] -[ComImport] -[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1212:Property accessors should follow order", Justification = "The order of the property accessors must match the order in which the methods were defined in the vtable")] -public interface ISearchCatalogManager +[GeneratedComInterface(StringMarshalling = StringMarshalling.Utf16)] +[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "Please do not change the function name")] +public partial interface ISearchCatalogManager { - [DispId(1610678272)] - string Name - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.LPWStr)] - get; - } + string get_Name(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - IntPtr GetParameter([MarshalAs(UnmanagedType.LPWStr), In] string pszName); + void GetParameter(string pszName, out IntPtr pValue); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - void SetParameter([MarshalAs(UnmanagedType.LPWStr), In] string pszName, [In] ref object pValue); + void SetParameter(string pszName, ref IntPtr pValue); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - void GetCatalogStatus(out object pStatus, out object pPausedReason); + void GetCatalogStatus(out uint pdwStatus, out uint pdwPausedReason); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] void Reset(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] void Reindex(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - void ReindexMatchingURLs([MarshalAs(UnmanagedType.LPWStr), In] string pszPattern); + void ReindexMatchingURLs(string pszPattern); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - void ReindexSearchRoot([MarshalAs(UnmanagedType.LPWStr), In] string pszRoot); + void ReindexSearchRoot(string pszRoot); - [DispId(1610678280)] - uint ConnectTimeout - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } + uint get_ConnectTimeout(); - [DispId(1610678282)] - uint DataTimeout - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } + void put_ConnectTimeout(uint dwTimeout); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - int NumberOfItems(); + uint get_DataTimeout(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - void NumberOfItemsToIndex( - out int plIncrementalCount, - out int plNotificationQueue, - out int plHighPriorityQueue); + void put_DataTimeout(uint dwTimeout); + + uint NumberOfItems(); + + uint NumberOfItemsToIndex(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] [return: MarshalAs(UnmanagedType.LPWStr)] string URLBeingIndexed(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - uint GetURLIndexingState([MarshalAs(UnmanagedType.LPWStr), In] string psz); + void GetURLIndexingState(string pszURL, out uint pdwState); + + IntPtr GetPersistentItemsChangedSink(); + + void RegisterViewForNotification(string pszView, IntPtr pViewNotify, out uint pdwCookie); + + IntPtr GetItemsChangedSink(); + + void UnregisterViewForNotification(uint dwCookie); + + void SetExtensionClusion(string pszExtension, [MarshalAs(UnmanagedType.Bool)] bool fExclude); + + void EnumerateExcludedExtensions(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] [return: MarshalAs(UnmanagedType.Interface)] - object GetPersistentItemsChangedSink(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - void RegisterViewForNotification( - [MarshalAs(UnmanagedType.LPWStr), In] string pszView, - [MarshalAs(UnmanagedType.Interface), In] object pViewChangedSink, - out uint pdwCookie); + ISearchQueryHelper GetQueryHelper(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - void GetItemsChangedSink( - [MarshalAs(UnmanagedType.Interface), In] object pISearchNotifyInlineSite, - [In] ref Guid riid, - out IntPtr ppv, - out Guid pGUIDCatalogResetSignature, - out Guid pGUIDCheckPointSignature, - out uint pdwLastCheckPointNumber); + [return: MarshalAs(UnmanagedType.Bool)] + bool get_DiacriticSensitivity(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - void UnregisterViewForNotification([In] uint dwCookie); + void put_DiacriticSensitivity([MarshalAs(UnmanagedType.Bool)] bool fDiacriticSensitive); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - void SetExtensionClusion([MarshalAs(UnmanagedType.LPWStr), In] string pszExtension, [In] int fExclude); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.Interface)] - object EnumerateExcludedExtensions(); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.Interface)] - CSearchQueryHelper GetQueryHelper(); - - [DispId(1610678295)] - int DiacriticSensitivity - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.Interface)] - object GetCrawlScopeManager(); + IntPtr GetCrawlScopeManager(); } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ISearchManager.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ISearchManager.cs index 042e0f7660..c8e7a37544 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ISearchManager.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ISearchManager.cs @@ -3,87 +3,46 @@ // See the LICENSE file in the project root for more information. using System; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch; -[ComConversionLoss] [Guid("AB310581-AC80-11D1-8DF3-00C04FB6EF69")] -[InterfaceType(1)] -[ComImport] -public interface ISearchManager +[GeneratedComInterface(StringMarshalling = StringMarshalling.Utf16)] +[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "Please do not change the function name")] +public partial interface ISearchManager { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - void GetIndexerVersionStr([MarshalAs(UnmanagedType.LPWStr)] out string ppszVersionString); + string GetIndexerVersionStr(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] void GetIndexerVersion(out uint pdwMajor, out uint pdwMinor); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - IntPtr GetParameter([MarshalAs(UnmanagedType.LPWStr), In] string pszName); + void GetParameter(string pszName, [MarshalAs(UnmanagedType.Interface)] out object pValue); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - void SetParameter([MarshalAs(UnmanagedType.LPWStr), In] string pszName, [In] ref object pValue); + void SetParameter(string pszName, [MarshalAs(UnmanagedType.Interface)] ref object pValue); - [DispId(1610678276)] - string ProxyName - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.LPWStr)] - get; - } + [return: MarshalAs(UnmanagedType.Bool)] + bool get_UseProxy(); - [DispId(1610678277)] - string BypassList - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.LPWStr)] - get; - } + string get_BypassList(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] void SetProxy( - [In] object sUseProxy, - [In] int fLocalByPassProxy, - [In] uint dwPortNumber, - [MarshalAs(UnmanagedType.LPWStr), In] string pszProxyName, - [MarshalAs(UnmanagedType.LPWStr), In] string pszByPassList); + string pszProxyName, + [MarshalAs(UnmanagedType.Bool)] bool fLocalBypass, + string pszBypassList, + uint dwPortNumber); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] [return: MarshalAs(UnmanagedType.Interface)] - CSearchCatalogManager GetCatalog([MarshalAs(UnmanagedType.LPWStr), In] string pszCatalog); + ISearchCatalogManager GetCatalog(string pszCatalog); - [DispId(1610678280)] - string UserAgent - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.LPWStr)] - get; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: MarshalAs(UnmanagedType.LPWStr)] - [param: In] - set; - } + string get_UserAgent(); - [DispId(1610678282)] - object UseProxy - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } + void put_UserAgent(string pszUserAgent); - [DispId(1610678283)] - int LocalBypass - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } + string get_ProxyName(); - [DispId(1610678284)] - uint PortNumber - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } + [return: MarshalAs(UnmanagedType.Bool)] + bool get_LocalBypass(); + + uint get_PortNumber(); } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ISearchQueryHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ISearchQueryHelper.cs index e560935639..d1a49e83bd 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ISearchQueryHelper.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ISearchQueryHelper.cs @@ -5,130 +5,74 @@ using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch; [Guid("AB310581-AC80-11D1-8DF3-00C04FB6EF63")] -[InterfaceType(1)] -[ComImport] -[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1212:Property accessors should follow order", Justification = "The order of the property accessors must match the order in which the methods were defined in the vtable")] -public interface ISearchQueryHelper +[GeneratedComInterface(StringMarshalling = StringMarshalling.Utf16)] +[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "I don't want to change the name")] +public partial interface ISearchQueryHelper { - [DispId(1610678272)] - string ConnectionString - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.LPWStr)] - get; - } + string GetConnectionString(); - [DispId(1610678273)] - uint QueryContentLocale - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } + void SetQueryContentLocale(int lcid); - [DispId(1610678275)] - uint QueryKeywordLocale - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } + uint GetQueryContentLocale(); - [DispId(1610678277)] - object QueryTermExpansion - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } + void SetQueryKeywordLocale(int lcid); - [DispId(1610678279)] - object QuerySyntax - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } + uint GetQueryKeywordLocale(); - [DispId(1610678281)] - string QueryContentProperties - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: MarshalAs(UnmanagedType.LPWStr)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.LPWStr)] - get; - } + void SetQueryTermExpansion(SEARCH_TERM_EXPANSION expandTerms); - [DispId(1610678283)] - string QuerySelectColumns - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: MarshalAs(UnmanagedType.LPWStr)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.LPWStr)] - get; - } + void GetQueryTermExpansion(out SEARCH_TERM_EXPANSION pExpandTerms); - [DispId(1610678285)] - string QueryWhereRestrictions - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: MarshalAs(UnmanagedType.LPWStr)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.LPWStr)] - get; - } + void SetQuerySyntax(SEARCH_QUERY_SYNTAX querySyntax); - [DispId(1610678287)] - string QuerySorting - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: MarshalAs(UnmanagedType.LPWStr)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.LPWStr)] - get; - } + [return: MarshalAs(UnmanagedType.Interface)] + object GetQuerySyntax(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.LPWStr)] - string GenerateSQLFromUserQuery([MarshalAs(UnmanagedType.LPWStr), In] string pszQuery); + void SetQueryContentProperties(string pszContentProperties); + + string GetQueryContentProperties(); + + void SetQuerySelectColumns(string pszColumns); + + string GetQuerySelectColumns(); + + void SetQueryWhereRestrictions(string pszRestrictions); + + string GetQueryWhereRestrictions(); + + void SetQuerySorting(string pszSorting); + + string GetQuerySorting(); + + string GenerateSQLFromUserQuery(string pszQuery); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] void WriteProperties( - [In] int itemID, - [In] uint dwNumberOfColumns, - [In] ref object pColumns, - [In] ref object pValues, - [In] ref object pftGatherModifiedTime); + int itemID, + uint dwNumberOfColumns, + [MarshalAs(UnmanagedType.Interface)] ref object pColumns, + [MarshalAs(UnmanagedType.Interface)] ref object pValues, + [MarshalAs(UnmanagedType.Interface)] ref object pftGatherModifiedTime); - [DispId(1610678291)] - int QueryMaxResults - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } + void SetQueryMaxResults(int lMaxResults); + + int GetQueryMaxResults(); +} + +public enum SEARCH_TERM_EXPANSION +{ + SEARCH_TERM_NO_EXPANSION, + SEARCH_TERM_PREFIX_ALL, + SEARCH_TERM_STEM_ALL, +} + +public enum SEARCH_QUERY_SYNTAX +{ + SEARCH_NO_QUERY_SYNTAX, + SEARCH_ADVANCED_QUERY_SYNTAX, + SEARCH_NATURAL_QUERY_SYNTAX, } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/PropVariant.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/PropVariant.cs new file mode 100644 index 0000000000..0a48a42ee5 --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/PropVariant.cs @@ -0,0 +1,70 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using VARTYPE = System.Runtime.InteropServices.VarEnum; + +namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch; + +#pragma warning disable SA1307 // Accessible fields should begin with upper-case letter +[StructLayout(LayoutKind.Explicit, Pack = 8)] +public struct PropVariant +{ + /// Value type tag. + [FieldOffset(0)] + public ushort vt; + + [FieldOffset(2)] + public ushort wReserved1; + + /// Reserved for future use. + [FieldOffset(4)] + public ushort wReserved2; + + /// Reserved for future use. + [FieldOffset(6)] + public ushort wReserved3; + + /// The decimal value when VT_DECIMAL. + [FieldOffset(0)] + internal decimal _decimal; + + /// The raw data pointer. + [FieldOffset(8)] + internal IntPtr _ptr; + + /// The FILETIME when VT_FILETIME. + [FieldOffset(8)] + internal System.Runtime.InteropServices.ComTypes.FILETIME _ft; + + [FieldOffset(8)] + internal BLOB _blob; + + /// The value when a numeric value less than 8 bytes. + [FieldOffset(8)] + internal ulong _ulong; + + public PropVariant(string value) + { + ArgumentNullException.ThrowIfNull(value); + vt = (ushort)VarEnum.VT_LPWSTR; + _ptr = Marshal.StringToCoTaskMemUni(value); + } + + public VarEnum VarType { get => (VarEnum)vt; set => vt = (ushort)(VARTYPE)value; } +} + +[StructLayout(LayoutKind.Sequential, Pack = 0)] +public struct BLOB +{ + /// The count of bytes + public uint cbSize; + + /// A pointer to the allocated array of bytes. + public IntPtr pBlobData; +} + +#pragma warning restore SA1307 // Accessible fields should begin with upper-case letter diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/PropertyKey.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/PropertyKey.cs new file mode 100644 index 0000000000..e37177d759 --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/PropertyKey.cs @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch; + +[StructLayout(LayoutKind.Sequential)] +public struct PropertyKey +{ + public Guid FmtID; + + public uint PID; + + public PropertyKey(Guid fmtid, uint pid) + { + this.FmtID = fmtid; + this.PID = pid; + } +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/Utils/QueryStringBuilder.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/Utils/QueryStringBuilder.cs index f835acafaa..e025492de6 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/Utils/QueryStringBuilder.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/Utils/QueryStringBuilder.cs @@ -2,12 +2,17 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; using Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch; +using Microsoft.CmdPal.Ext.Indexer.Native; namespace Microsoft.CmdPal.Ext.Indexer.Indexer.Utils; -internal sealed class QueryStringBuilder +internal sealed partial class QueryStringBuilder { private const string Properties = "System.ItemUrl, System.ItemNameDisplay, path, System.Search.EntryID, System.Kind, System.KindText"; private const string SystemIndex = "SystemIndex"; @@ -24,16 +29,40 @@ internal sealed class QueryStringBuilder { if (queryHelper == null) { - var searchManager = new CSearchManager(); - ISearchCatalogManager catalogManager = searchManager.GetCatalog(SystemIndex); - queryHelper = catalogManager.GetQueryHelper(); + ComWrappers cw = new StrategyBasedComWrappers(); - queryHelper.QuerySelectColumns = Properties; - queryHelper.QueryContentProperties = "System.FileName"; - queryHelper.QuerySorting = OrderConditions; + var hr = NativeMethods.CoCreateInstance(ref Unsafe.AsRef(in NativeHelpers.CsWin32GUID.CLSIDSearchManager), IntPtr.Zero, NativeHelpers.CLSCTXINPROCALL, ref Unsafe.AsRef(in NativeHelpers.CsWin32GUID.IIDISearchManager), out var searchManagerPtr); + if (hr != 0) + { + throw new ArgumentException($"Failed to create SearchManager instance. HR: 0x{hr:X}"); + } + + var searchManager = (ISearchManager)cw.GetOrCreateObjectForComInstance( + searchManagerPtr, CreateObjectFlags.None); + + if (searchManager == null) + { + throw new ArgumentException("Failed to get ISearchManager interface"); + } + + ISearchCatalogManager catalogManager = searchManager.GetCatalog(SystemIndex); + if (catalogManager == null) + { + throw new ArgumentException($"Failed to get catalog manager for {SystemIndex}"); + } + + queryHelper = catalogManager.GetQueryHelper(); + if (queryHelper == null) + { + throw new ArgumentException("Failed to get query helper from catalog manager"); + } + + queryHelper.SetQuerySelectColumns(Properties); + queryHelper.SetQueryContentProperties("System.FileName"); + queryHelper.SetQuerySorting(OrderConditions); } - queryHelper.QueryWhereRestrictions = "AND " + ScopeFileConditions + "AND ReuseWhere(" + whereId.ToString(CultureInfo.InvariantCulture) + ")"; + queryHelper.SetQueryWhereRestrictions("AND " + ScopeFileConditions + "AND ReuseWhere(" + whereId.ToString(CultureInfo.InvariantCulture) + ")"); return queryHelper.GenerateSQLFromUserQuery(searchText); } } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Microsoft.CmdPal.Ext.Indexer.csproj b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Microsoft.CmdPal.Ext.Indexer.csproj index 70e6a10c5a..73102cbc31 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Microsoft.CmdPal.Ext.Indexer.csproj +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Microsoft.CmdPal.Ext.Indexer.csproj @@ -1,12 +1,12 @@ - + + Microsoft.CmdPal.Ext.Indexer - $(SolutionDir)$(Platform)\$(Configuration)\WinUI3Apps\CmdPal false false - + true @@ -46,4 +46,9 @@ + + + + + diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Native/NativeHelpers.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Native/NativeHelpers.cs index 9851573843..e7d7f2ca75 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Native/NativeHelpers.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Native/NativeHelpers.cs @@ -1,25 +1,34 @@ // Copyright (c) Microsoft Corporation // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. - using System; -using Windows.Win32.UI.Shell.PropertiesSystem; +using Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch; namespace Microsoft.CmdPal.Ext.Indexer.Native; -internal sealed class NativeHelpers +public sealed partial class NativeHelpers { public const uint SEEMASKINVOKEIDLIST = 12; - internal static class PropertyKeys + public const uint CLSCTXINPROCALL = 0x17; + + public struct PropertyKeys { - public static readonly PROPERTYKEY PKEYItemNameDisplay = new() { fmtid = new System.Guid("B725F130-47EF-101A-A5F1-02608C9EEBAC"), pid = 10 }; - public static readonly PROPERTYKEY PKEYItemUrl = new() { fmtid = new System.Guid("49691C90-7E17-101A-A91C-08002B2ECDA9"), pid = 9 }; - public static readonly PROPERTYKEY PKEYKindText = new() { fmtid = new System.Guid("F04BEF95-C585-4197-A2B7-DF46FDC9EE6D"), pid = 100 }; + public static readonly PropertyKey PKEYItemNameDisplay = new() { FmtID = new System.Guid("B725F130-47EF-101A-A5F1-02608C9EEBAC"), PID = 10 }; + public static readonly PropertyKey PKEYItemUrl = new() { FmtID = new System.Guid("49691C90-7E17-101A-A91C-08002B2ECDA9"), PID = 9 }; + public static readonly PropertyKey PKEYKindText = new() { FmtID = new System.Guid("F04BEF95-C585-4197-A2B7-DF46FDC9EE6D"), PID = 100 }; } - internal static class OleDb + public static class OleDb { public static readonly Guid DbGuidDefault = new("C8B521FB-5CF3-11CE-ADE5-00AA0044773D"); } + + public static class CsWin32GUID + { + public static readonly Guid CLSIDSearchManager = new Guid("7D096C5F-AC08-4F1F-BEB7-5C22C517CE39"); + public static readonly Guid IIDISearchManager = new Guid("AB310581-AC80-11D1-8DF3-00C04FB6EF69"); + public static readonly Guid CLSIDCollatorDataSource = new Guid("9E175B8B-F52A-11D8-B9A5-505054503030"); + public static readonly Guid PropertyStore = new Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99"); + } } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Native/NativeMethods.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Native/NativeMethods.cs new file mode 100644 index 0000000000..12b97a813e --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Native/NativeMethods.cs @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; +using Windows.Win32.UI.Shell; + +namespace Microsoft.CmdPal.Ext.Indexer.Native; + +public sealed partial class NativeMethods +{ + [LibraryImport("ole32.dll")] + [return: MarshalAs(UnmanagedType.U4)] + public static partial uint CoCreateInstance( + ref Guid rclsid, + IntPtr pUnkOuter, + uint dwClsContext, + ref Guid riid, + out IntPtr rReturnedComObject); + + [LibraryImport("SHELL32.dll", EntryPoint = "ShellExecuteExW", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static partial bool ShellExecuteEx(ref SHELLEXECUTEINFOW lpExecInfo); + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct SHELLEXECUTEINFOW + { + public uint cbSize; + public uint fMask; + public IntPtr hwnd; + + public IntPtr lpVerb; + public IntPtr lpFile; + public IntPtr lpParameters; + public IntPtr lpDirectory; + public int nShow; + public IntPtr hInstApp; + public IntPtr lpIDList; + public IntPtr lpClass; + public IntPtr hkeyClass; + public uint dwHotKey; + public IntPtr hIconOrMonitor; + public IntPtr hProcess; + } +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/NativeMethods.json b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/NativeMethods.json new file mode 100644 index 0000000000..aa6eed881c --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/NativeMethods.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://aka.ms/CsWin32.schema.json", + "allowMarshaling": false, + "emitSingleFile": false, + "public": true +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/NativeMethods.txt b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/NativeMethods.txt index f0702ba24c..029b6fe6c2 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/NativeMethods.txt +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/NativeMethods.txt @@ -1,11 +1,2 @@ DBID -SHOW_WINDOW_CMD -CoCreateInstance -GetErrorInfo -ICommandText -IDBCreateCommand -IDBCreateSession -IDBInitialize -IGetRow -IPropertyStore -ShellExecuteEx \ No newline at end of file +SHOW_WINDOW_CMD \ No newline at end of file From 79958975b4d3d708ec69e467ece39b216f0ea833 Mon Sep 17 00:00:00 2001 From: leileizhang Date: Wed, 4 Jun 2025 19:30:11 +0800 Subject: [PATCH 036/117] [AOT][CmdPal] Enable NativeAOT Compatibility for CmdPal Apps Extension (#39678) ## Summary of the Pull Request This PR introduces necessary changes to make the CmdPal.Apps extension compatible with NativeAOT publishing. The main updates include: 1. Project configuration updates: Added NativeAOT-related properties to the .csproj. 2. Native interop adjustments: > - Introduced NativeMethods.json and used [CsWin32](https://github.com/microsoft/cswin32) to generate P/Invoke bindings. > - Replaced some DllImport declarations with source-generated [LibraryImport] for improved AOT support. --- .github/actions/spell-check/expect.txt | 3 + src/Common.Dotnet.AotCompatibility.props | 2 +- .../AllAppsSettings.cs | 2 +- .../Microsoft.CmdPal.Ext.Apps/AppCommand.cs | 32 ++- .../Microsoft.CmdPal.Ext.Apps.csproj | 7 + .../NativeMethods.json | 7 + .../NativeMethods.txt | 35 ++-- .../Programs/AppxFactory.cs | 14 -- .../Programs/AppxPackageHelper.cs | 77 +++++-- .../Programs/IApplicationActivationManager.cs | 47 ----- .../Programs/IAppxFactory.cs | 20 -- .../Programs/IAppxManifestApplication.cs | 19 -- .../IAppxManifestApplicationsEnumerator.cs | 19 -- .../Programs/IAppxManifestProperties.cs | 19 -- .../Programs/IAppxManifestReader.cs | 27 --- .../Programs/ReparsePoint.cs | 127 ++++++------ .../Microsoft.CmdPal.Ext.Apps/Programs/UWP.cs | 86 ++++---- .../Programs/UWPApplication.cs | 97 ++++----- .../Programs/Win32Program.cs | 74 ++++++- .../Storage/ListRepository`1.cs | 5 - .../Storage/Win32ProgramRepository.cs | 6 +- .../Utils/ComFreeHelper.cs | 35 ++++ .../Microsoft.CmdPal.Ext.Apps/Utils/Native.cs | 157 -------------- .../Utils/SafeComHandle.cs | 30 +++ .../Utils/ShellLinkHelper.cs | 196 ++++-------------- .../Utils/ShellLocalization.cs | 34 ++- 26 files changed, 477 insertions(+), 700 deletions(-) create mode 100644 src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/NativeMethods.json delete mode 100644 src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/AppxFactory.cs delete mode 100644 src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IApplicationActivationManager.cs delete mode 100644 src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxFactory.cs delete mode 100644 src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestApplication.cs delete mode 100644 src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestApplicationsEnumerator.cs delete mode 100644 src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestProperties.cs delete mode 100644 src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestReader.cs create mode 100644 src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/ComFreeHelper.cs delete mode 100644 src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/Native.cs create mode 100644 src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/SafeComHandle.cs diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 6dd1d3577e..569cdb93db 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -14,6 +14,7 @@ AColumn acrt ACTIVATEAPP activationaction +ACTIVATEOPTIONS ACVS adaptivecards ADate @@ -1588,6 +1589,7 @@ steamapps STGC STGM STGMEDIUM +STGMREAD STICKYKEYS sticpl storelogo @@ -1692,6 +1694,7 @@ TLayout tlb tlbimp tlc +TGM TNP Toolhelp toolkitconverters diff --git a/src/Common.Dotnet.AotCompatibility.props b/src/Common.Dotnet.AotCompatibility.props index 46a39e9c65..82988104dd 100644 --- a/src/Common.Dotnet.AotCompatibility.props +++ b/src/Common.Dotnet.AotCompatibility.props @@ -7,6 +7,6 @@ 2 - IL2081;$(WarningsNotAsErrors) + IL2081;CsWinRT1028;$(WarningsNotAsErrors) diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AllAppsSettings.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AllAppsSettings.cs index 91d5cd5736..63a40614aa 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AllAppsSettings.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AllAppsSettings.cs @@ -71,7 +71,7 @@ public class AllAppsSettings : JsonSettingsManager internal static string SettingsJsonPath() { - string directory = Utilities.BaseSettingsPath("Microsoft.CmdPal"); + var directory = Utilities.BaseSettingsPath("Microsoft.CmdPal"); Directory.CreateDirectory(directory); // now, the state is just next to the exe diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AppCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AppCommand.cs index cb57086cb3..d2fe194830 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AppCommand.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AppCommand.cs @@ -8,7 +8,12 @@ using System.Threading.Tasks; using ManagedCommon; using Microsoft.CmdPal.Ext.Apps.Programs; using Microsoft.CmdPal.Ext.Apps.Properties; +using Microsoft.CmdPal.Ext.Apps.Utils; using Microsoft.CommandPalette.Extensions.Toolkit; +using Windows.Services.Maps; +using Windows.Win32; +using Windows.Win32.System.Com; +using Windows.Win32.UI.Shell; using WyHash; namespace Microsoft.CmdPal.Ext.Apps; @@ -27,26 +32,31 @@ internal sealed partial class AppCommand : InvokableCommand internal static async Task StartApp(string aumid) { - var appManager = new ApplicationActivationManager(); - const ActivateOptions noFlags = ActivateOptions.None; await Task.Run(() => { - try + unsafe { - appManager.ActivateApplication(aumid, /*queryArguments*/ string.Empty, noFlags, out var unusedPid); - } - catch (System.Exception ex) - { - Logger.LogError(ex.Message); + IApplicationActivationManager* appManager = null; + try + { + PInvoke.CoCreateInstance(typeof(ApplicationActivationManager).GUID, null, CLSCTX.CLSCTX_INPROC_SERVER, out appManager).ThrowOnFailure(); + using var handle = new SafeComHandle((IntPtr)appManager); + appManager->ActivateApplication( + aumid, + string.Empty, + ACTIVATEOPTIONS.AO_NONE, + out var unusedPid); + } + catch (System.Exception ex) + { + Logger.LogError(ex.Message); + } } }).ConfigureAwait(false); } internal static async Task StartExe(string path) { - var appManager = new ApplicationActivationManager(); - - // const ActivateOptions noFlags = ActivateOptions.None; await Task.Run(() => { try diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Microsoft.CmdPal.Ext.Apps.csproj b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Microsoft.CmdPal.Ext.Apps.csproj index 24782335ed..e5c3de2ac4 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Microsoft.CmdPal.Ext.Apps.csproj +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Microsoft.CmdPal.Ext.Apps.csproj @@ -1,11 +1,13 @@  + Microsoft.CmdPal.Ext.Apps enable $(SolutionDir)$(Platform)\$(Configuration)\WinUI3Apps\CmdPal false false + true @@ -49,4 +51,9 @@ Resources.Designer.cs + + + + + diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/NativeMethods.json b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/NativeMethods.json new file mode 100644 index 0000000000..b1156c41b7 --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/NativeMethods.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/CsWin32.schema.json", + "allowMarshaling": false, + "comInterop": { + "preserveSigMethods": [ "*" ] + } +} \ No newline at end of file diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/NativeMethods.txt b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/NativeMethods.txt index c0c94348c5..017871d42f 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/NativeMethods.txt +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/NativeMethods.txt @@ -1,19 +1,20 @@ -GetPhysicallyInstalledSystemMemory -GlobalMemoryStatusEx -GetSystemInfo +IStream CoCreateInstance -SetForegroundWindow -IsIconic -RegisterHotKey -SetWindowLongPtr -CallWindowProc -ShowWindow -SetForegroundWindow -SetFocus -SetActiveWindow -MonitorFromWindow -GetMonitorInfo +IApplicationActivationManager +ApplicationActivationManager SHCreateStreamOnFileEx -CoAllowSetForegroundWindow -SHCreateStreamOnFileEx -SHLoadIndirectString \ No newline at end of file +SHCreateItemFromParsingName +IShellItem +ISequentialStream +SHLoadIndirectString +IAppxFactory +AppxFactory +IAppxManifestReader +IAppxManifestApplicationsEnumerator +IAppxManifestApplication +IAppxManifestProperties +IShellLinkW +ShellLink +IPersistFile +CoTaskMemFree +IUnknown diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/AppxFactory.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/AppxFactory.cs deleted file mode 100644 index 420128ef29..0000000000 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/AppxFactory.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Runtime.InteropServices; - -namespace Microsoft.CmdPal.Ext.Apps.Programs; - -// Reference : https://stackoverflow.com/questions/32122679/getting-icon-of-modern-windows-app-from-a-desktop-application -[Guid("5842a140-ff9f-4166-8f5c-62f5b7b0c781")] -[ComImport] -public class AppxFactory -{ -} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/AppxPackageHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/AppxPackageHelper.cs index 83a9fbb146..ef81410898 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/AppxPackageHelper.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/AppxPackageHelper.cs @@ -2,42 +2,75 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections.Generic; -using System.Runtime.InteropServices; +using ManagedCommon; +using Microsoft.CmdPal.Ext.Apps.Utils; +using Microsoft.UI.Xaml.Controls; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.Storage.Packaging.Appx; using Windows.Win32.System.Com; -using static Microsoft.CmdPal.Ext.Apps.Utils.Native; namespace Microsoft.CmdPal.Ext.Apps.Programs; public static class AppxPackageHelper { - private static readonly IAppxFactory AppxFactory = (IAppxFactory)new AppxFactory(); - - // This function returns a list of attributes of applications - internal static IEnumerable GetAppsFromManifest(IStream stream) + internal static unsafe List GetAppsFromManifest(IStream* stream) { - var reader = AppxFactory.CreateManifestReader(stream); - var manifestApps = reader.GetApplications(); + PInvoke.CoCreateInstance(typeof(AppxFactory).GUID, null, CLSCTX.CLSCTX_INPROC_SERVER, out IAppxFactory* appxFactory).ThrowOnFailure(); + using var handle = new SafeComHandle((IntPtr)appxFactory); - while (manifestApps.GetHasCurrent()) + IAppxManifestReader* reader = null; + IAppxManifestApplicationsEnumerator* manifestApps = null; + var result = new List(); + + appxFactory->CreateManifestReader(stream, &reader); + using var readerHandle = new SafeComHandle((IntPtr)reader); + reader->GetApplications(&manifestApps); + using var manifestAppsHandle = new SafeComHandle((IntPtr)manifestApps); + + while (true) { - var manifestApp = manifestApps.GetCurrent(); - var hr = manifestApp.GetStringValue("AppListEntry", out var appListEntry); - _ = CheckHRAndReturnOrThrow(hr, appListEntry); - if (appListEntry != "none") + manifestApps->GetHasCurrent(out var hasCurrent); + if (hasCurrent == false) { - yield return manifestApp; + break; } - manifestApps.MoveNext(); - } - } + IAppxManifestApplication* manifestApp = null; - internal static T CheckHRAndReturnOrThrow(HRESULT hr, T result) - { - if (hr != HRESULT.S_OK) - { - Marshal.ThrowExceptionForHR((int)hr); + try + { + manifestApps->GetCurrent(&manifestApp).ThrowOnFailure(); + + var hr = manifestApp->GetStringValue("AppListEntry", out var appListEntryPtr); + var appListEntry = ComFreeHelper.GetStringAndFree(hr, appListEntryPtr); + + if (appListEntry != "none") + { + result.Add((IntPtr)manifestApp); + } + else if (manifestApp != null) + { + manifestApp->Release(); + } + } + catch (Exception ex) + { + if (manifestApp != null) + { + manifestApp->Release(); + } + + Logger.LogError($"Failed to get current application from manifest: {ex.Message}"); + } + + manifestApps->MoveNext(out var hasNext); + if (hasNext == false) + { + break; + } } return result; diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IApplicationActivationManager.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IApplicationActivationManager.cs deleted file mode 100644 index 32fb3f2890..0000000000 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IApplicationActivationManager.cs +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace Microsoft.CmdPal.Ext.Apps.Programs; - -// Reference : https://github.com/MicrosoftEdge/edge-launcher/blob/108e63df0b4cb5cd9d5e45aa7a264690851ec51d/MIcrosoftEdgeLauncherCsharp/Program.cs -[Flags] -public enum ActivateOptions -{ - None = 0x00000000, - DesignMode = 0x00000001, - NoErrorUI = 0x00000002, - NoSplashScreen = 0x00000004, -} - -// ApplicationActivationManager -[ComImport] -[Guid("2e941141-7f97-4756-ba1d-9decde894a3d")] -[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] -public interface IApplicationActivationManager -{ - IntPtr ActivateApplication([In] string appUserModelId, [In] string arguments, [In] ActivateOptions options, [Out] out uint processId); - - IntPtr ActivateForFile([In] string appUserModelId, [In] IntPtr /*IShellItemArray* */ itemArray, [In] string verb, [Out] out uint processId); - - IntPtr ActivateForProtocol([In] string appUserModelId, [In] IntPtr /* IShellItemArray* */itemArray, [Out] out uint processId); -} - -// Application Activation Manager Class -[ComImport] -[Guid("45BA127D-10A8-46EA-8AB7-56EA9078943C")] -public class ApplicationActivationManager : IApplicationActivationManager -{ - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)/*, PreserveSig*/] - public extern IntPtr ActivateApplication([In] string appUserModelId, [In] string arguments, [In] ActivateOptions options, [Out] out uint processId); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public extern IntPtr ActivateForFile([In] string appUserModelId, [In] IntPtr /*IShellItemArray* */ itemArray, [In] string verb, [Out] out uint processId); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public extern IntPtr ActivateForProtocol([In] string appUserModelId, [In] IntPtr /* IShellItemArray* */itemArray, [Out] out uint processId); -} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxFactory.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxFactory.cs deleted file mode 100644 index 7af82b74ab..0000000000 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxFactory.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Runtime.InteropServices; -using Windows.Win32.System.Com; - -namespace Microsoft.CmdPal.Ext.Apps.Programs; - -[Guid("BEB94909-E451-438B-B5A7-D79E767B75D8")] -[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] -public interface IAppxFactory -{ - [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "Implements COM Interface")] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Implements COM Interface")] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Implements COM Interface")] - void _VtblGap0_2(); // skip 2 methods - - internal IAppxManifestReader CreateManifestReader(IStream inputStream); -} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestApplication.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestApplication.cs deleted file mode 100644 index 1ca12d3c29..0000000000 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestApplication.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Runtime.InteropServices; -using static Microsoft.CmdPal.Ext.Apps.Utils.Native; - -namespace Microsoft.CmdPal.Ext.Apps.Programs; - -[Guid("5DA89BF4-3773-46BE-B650-7E744863B7E8")] -[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] -public interface IAppxManifestApplication -{ - [PreserveSig] - HRESULT GetStringValue([MarshalAs(UnmanagedType.LPWStr)] string name, [MarshalAs(UnmanagedType.LPWStr)] out string value); - - [PreserveSig] - HRESULT GetAppUserModelId([MarshalAs(UnmanagedType.LPWStr)] out string value); -} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestApplicationsEnumerator.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestApplicationsEnumerator.cs deleted file mode 100644 index f7152a0813..0000000000 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestApplicationsEnumerator.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Runtime.InteropServices; - -namespace Microsoft.CmdPal.Ext.Apps.Programs; - -[Guid("9EB8A55A-F04B-4D0D-808D-686185D4847A")] -[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] -public interface IAppxManifestApplicationsEnumerator -{ - IAppxManifestApplication GetCurrent(); - - bool GetHasCurrent(); - - bool MoveNext(); -} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestProperties.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestProperties.cs deleted file mode 100644 index 4c61e6f069..0000000000 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestProperties.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Runtime.InteropServices; -using static Microsoft.CmdPal.Ext.Apps.Utils.Native; - -namespace Microsoft.CmdPal.Ext.Apps.Programs; - -[Guid("03FAF64D-F26F-4B2C-AAF7-8FE7789B8BCA")] -[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] -public interface IAppxManifestProperties -{ - [PreserveSig] - HRESULT GetBoolValue([MarshalAs(UnmanagedType.LPWStr)] string name, out bool value); - - [PreserveSig] - HRESULT GetStringValue([MarshalAs(UnmanagedType.LPWStr)] string name, [MarshalAs(UnmanagedType.LPWStr)] out string value); -} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestReader.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestReader.cs deleted file mode 100644 index 20c7fb62f6..0000000000 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestReader.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Runtime.InteropServices; - -namespace Microsoft.CmdPal.Ext.Apps.Programs; - -[Guid("4E1BD148-55A0-4480-A3D1-15544710637C")] -[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] -public interface IAppxManifestReader -{ - [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "Implements COM Interface")] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Implements COM Interface")] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Implements COM Interface")] - void _VtblGap0_1(); // skip 1 method - - IAppxManifestProperties GetProperties(); - - [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "Implements COM Interface")] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Implements COM Interface")] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Implements COM Interface")] - void _VtblGap1_5(); // skip 5 methods - - IAppxManifestApplicationsEnumerator GetApplications(); -} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/ReparsePoint.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/ReparsePoint.cs index 1e3463f77c..e10c84eea9 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/ReparsePoint.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/ReparsePoint.cs @@ -5,6 +5,7 @@ using System; using System.ComponentModel; using System.IO; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; @@ -16,7 +17,7 @@ namespace Microsoft.CmdPal.Ext.Apps.Programs; /// /// Provides access to NTFS reparse points in .Net. /// -public static class ReparsePoint +public static partial class ReparsePoint { #pragma warning disable SA1310 // Field names should not contain underscore @@ -36,7 +37,7 @@ public static class ReparsePoint #pragma warning restore SA1310 // Field names should not contain underscore [Flags] - private enum FileAccessType : uint + internal enum FileAccessType : uint { DELETE = 0x00010000, READ_CONTROL = 0x00020000, @@ -100,7 +101,7 @@ public static class ReparsePoint } [Flags] - private enum FileShareType : uint + internal enum FileShareType : uint { None = 0x00000000, Read = 0x00000001, @@ -108,7 +109,7 @@ public static class ReparsePoint Delete = 0x00000004, } - private enum CreationDisposition : uint + internal enum CreationDisposition : uint { New = 1, CreateAlways = 2, @@ -118,7 +119,7 @@ public static class ReparsePoint } [Flags] - private enum FileAttributes : uint + internal enum FileAttributes : uint { Readonly = 0x00000001, Hidden = 0x00000002, @@ -195,8 +196,9 @@ public static class ReparsePoint public AppExecutionAliasReparseTagBufferLayoutVersion Version; } - [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] - private static extern bool DeviceIoControl( + [LibraryImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + internal static partial bool DeviceIoControl( IntPtr hDevice, uint dwIoControlCode, IntPtr inBuffer, @@ -206,8 +208,8 @@ public static class ReparsePoint out int pBytesReturned, IntPtr lpOverlapped); - [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] - private static extern IntPtr CreateFile( + [LibraryImport("kernel32.dll", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] + internal static partial int CreateFile( string lpFileName, FileAccessType dwDesiredAccess, FileShareType dwShareMode, @@ -284,15 +286,18 @@ public static class ReparsePoint ThrowLastWin32Error("Unable to get information about reparse point."); } - AppExecutionAliasReparseTagHeader aliasReparseHeader = Marshal.PtrToStructure(outBuffer); - - if (aliasReparseHeader.ReparseTag == IO_REPARSE_TAG_APPEXECLINK) + unsafe { - var metadata = AppExecutionAliasMetadata.FromPersistedRepresentationIntPtr( - outBuffer, - aliasReparseHeader.Version); + var aliasReparseHeader = Unsafe.Read((void*)outBuffer); - return metadata.ExePath; + if (aliasReparseHeader.ReparseTag == IO_REPARSE_TAG_APPEXECLINK) + { + var metadata = AppExecutionAliasMetadata.FromPersistedRepresentationIntPtr( + outBuffer, + aliasReparseHeader.Version); + + return metadata.ExePath; + } } return null; @@ -319,61 +324,65 @@ public static class ReparsePoint public static AppExecutionAliasMetadata FromPersistedRepresentationIntPtr(IntPtr reparseDataBufferPtr, AppExecutionAliasReparseTagBufferLayoutVersion version) { - var dataOffset = Marshal.SizeOf(); - var dataBufferPtr = reparseDataBufferPtr + dataOffset; - - string? packageFullName = null; - string? packageFamilyName = null; - string? aumid = null; - string? exePath = null; - - VerifyVersion(version); - - switch (version) + unsafe { - case AppExecutionAliasReparseTagBufferLayoutVersion.Initial: - packageFullName = Marshal.PtrToStringUni(dataBufferPtr); - if (packageFullName is not null) - { - dataBufferPtr += Encoding.Unicode.GetByteCount(packageFullName) + Encoding.Unicode.GetByteCount("\0"); - aumid = Marshal.PtrToStringUni(dataBufferPtr); + var dataOffset = Unsafe.SizeOf(); - if (aumid is not null) + var dataBufferPtr = reparseDataBufferPtr + dataOffset; + + string? packageFullName = null; + string? packageFamilyName = null; + string? aumid = null; + string? exePath = null; + + VerifyVersion(version); + + switch (version) + { + case AppExecutionAliasReparseTagBufferLayoutVersion.Initial: + packageFullName = Marshal.PtrToStringUni(dataBufferPtr); + if (packageFullName is not null) { - dataBufferPtr += Encoding.Unicode.GetByteCount(aumid) + Encoding.Unicode.GetByteCount("\0"); - exePath = Marshal.PtrToStringUni(dataBufferPtr); + dataBufferPtr += Encoding.Unicode.GetByteCount(packageFullName) + Encoding.Unicode.GetByteCount("\0"); + aumid = Marshal.PtrToStringUni(dataBufferPtr); + + if (aumid is not null) + { + dataBufferPtr += Encoding.Unicode.GetByteCount(aumid) + Encoding.Unicode.GetByteCount("\0"); + exePath = Marshal.PtrToStringUni(dataBufferPtr); + } } - } - break; + break; - case AppExecutionAliasReparseTagBufferLayoutVersion.PackageFamilyName: - case AppExecutionAliasReparseTagBufferLayoutVersion.MultiAppTypeSupport: - packageFamilyName = Marshal.PtrToStringUni(dataBufferPtr); + case AppExecutionAliasReparseTagBufferLayoutVersion.PackageFamilyName: + case AppExecutionAliasReparseTagBufferLayoutVersion.MultiAppTypeSupport: + packageFamilyName = Marshal.PtrToStringUni(dataBufferPtr); - if (packageFamilyName is not null) - { - dataBufferPtr += Encoding.Unicode.GetByteCount(packageFamilyName) + Encoding.Unicode.GetByteCount("\0"); - aumid = Marshal.PtrToStringUni(dataBufferPtr); - - if (aumid is not null) + if (packageFamilyName is not null) { - dataBufferPtr += Encoding.Unicode.GetByteCount(aumid) + Encoding.Unicode.GetByteCount("\0"); + dataBufferPtr += Encoding.Unicode.GetByteCount(packageFamilyName) + Encoding.Unicode.GetByteCount("\0"); + aumid = Marshal.PtrToStringUni(dataBufferPtr); - exePath = Marshal.PtrToStringUni(dataBufferPtr); + if (aumid is not null) + { + dataBufferPtr += Encoding.Unicode.GetByteCount(aumid) + Encoding.Unicode.GetByteCount("\0"); + + exePath = Marshal.PtrToStringUni(dataBufferPtr); + } } - } - break; + break; + } + + return new AppExecutionAliasMetadata + { + PackageFullName = packageFullName ?? string.Empty, + PackageFamilyName = packageFamilyName ?? string.Empty, + Aumid = aumid ?? string.Empty, + ExePath = exePath ?? string.Empty, + }; } - - return new AppExecutionAliasMetadata - { - PackageFullName = packageFullName ?? string.Empty, - PackageFamilyName = packageFamilyName ?? string.Empty, - Aumid = aumid ?? string.Empty, - ExePath = exePath ?? string.Empty, - }; } private static void VerifyVersion(AppExecutionAliasReparseTagBufferLayoutVersion version) diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/UWP.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/UWP.cs index e115911001..15d7c079db 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/UWP.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/UWP.cs @@ -3,17 +3,19 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.IO.Abstractions; using System.Linq; +using System.Threading.Tasks; using System.Xml.Linq; using ManagedCommon; using Microsoft.CmdPal.Ext.Apps.Utils; using Microsoft.CommandPalette.Extensions.Toolkit; using Windows.Win32; using Windows.Win32.Foundation; +using Windows.Win32.Storage.Packaging.Appx; using Windows.Win32.System.Com; -using static Microsoft.CmdPal.Ext.Apps.Utils.Native; namespace Microsoft.CmdPal.Ext.Apps.Programs; @@ -55,7 +57,7 @@ public partial class UWP FamilyName = package.FamilyName; } - public void InitializeAppInfo(string installedLocation) + public unsafe void InitializeAppInfo(string installedLocation) { Location = installedLocation; LocationLocalized = ShellLocalization.Instance.GetLocalizedPath(installedLocation); @@ -65,26 +67,31 @@ public partial class UWP InitPackageVersion(namespaces); const uint noAttribute = 0x80; - - var access = (uint)STGM.READ; - var hResult = PInvoke.SHCreateStreamOnFileEx(path, access, noAttribute, false, null, out IStream stream); - - // S_OK - if (hResult == 0) + const uint STGMREAD = 0x00000000; + try { - Apps = AppxPackageHelper.GetAppsFromManifest(stream).Select(appInManifest => new UWPApplication(appInManifest, this)).Where(a => + IStream* stream = null; + PInvoke.SHCreateStreamOnFileEx(path, STGMREAD, noAttribute, false, null, &stream).ThrowOnFailure(); + using var streamHandle = new SafeComHandle((IntPtr)stream); + + Apps = AppxPackageHelper.GetAppsFromManifest(stream).Select(appInManifest => + { + using var appHandle = new SafeComHandle(appInManifest); + return new UWPApplication((IAppxManifestApplication*)appInManifest, this); + }).Where(a => { var valid = - !string.IsNullOrEmpty(a.UserModelId) && - !string.IsNullOrEmpty(a.DisplayName) && - a.AppListEntry != "none"; - + !string.IsNullOrEmpty(a.UserModelId) && + !string.IsNullOrEmpty(a.DisplayName) && + a.AppListEntry != "none"; return valid; }).ToList(); } - else + catch (Exception ex) { Apps = Array.Empty(); + Logger.LogError($"Failed to initialize UWP app info for {Name} ({FullName}): {ex.Message}"); + return; } } @@ -123,35 +130,36 @@ public partial class UWP { var windows10 = new Version(10, 0); var support = Environment.OSVersion.Version.Major >= windows10.Major; - if (support) - { - var applications = CurrentUserPackages().AsParallel().SelectMany(p => - { - UWP u; - try - { - u = new UWP(p); - u.InitializeAppInfo(p.InstalledLocation); - } - catch (Exception ex) - { - Logger.LogError(ex.Message); - return Array.Empty(); - } - return u.Apps; - }); - - var updatedListWithoutDisabledApps = applications - .Where(t1 => AllAppsSettings.Instance.DisabledProgramSources.All(x => x.UniqueIdentifier != t1.UniqueIdentifier)) - .Select(x => x); - - return updatedListWithoutDisabledApps.ToArray(); - } - else + if (!support) { return Array.Empty(); } + + var appsBag = new ConcurrentBag(); + + Parallel.ForEach(CurrentUserPackages(), p => + { + try + { + var u = new UWP(p); + u.InitializeAppInfo(p.InstalledLocation); + + foreach (var app in u.Apps) + { + if (AllAppsSettings.Instance.DisabledProgramSources.All(x => x.UniqueIdentifier != app.UniqueIdentifier)) + { + appsBag.Add(app); + } + } + } + catch (Exception ex) + { + Logger.LogError(ex.Message); + } + }); + + return appsBag.ToArray(); } private static IEnumerable CurrentUserPackages() diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/UWPApplication.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/UWPApplication.cs index c38c05d7b5..793000146d 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/UWPApplication.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/UWPApplication.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.IO.Abstractions; using System.Linq; +using System.Runtime.InteropServices; using System.Text; using System.Xml; using ManagedCommon; @@ -13,7 +14,9 @@ using Microsoft.CmdPal.Ext.Apps.Commands; using Microsoft.CmdPal.Ext.Apps.Properties; using Microsoft.CmdPal.Ext.Apps.Utils; using Microsoft.CommandPalette.Extensions.Toolkit; -using static Microsoft.CmdPal.Ext.Apps.Utils.Native; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.Storage.Packaging.Appx; using PackageVersion = Microsoft.CmdPal.Ext.Apps.Programs.UWP.PackageVersion; using Theme = Microsoft.CmdPal.Ext.Apps.Utils.Theme; @@ -97,27 +100,27 @@ public class UWPApplication : IProgram return commands; } - public UWPApplication(IAppxManifestApplication manifestApp, UWP package) + internal unsafe UWPApplication(IAppxManifestApplication* manifestApp, UWP package) { ArgumentNullException.ThrowIfNull(manifestApp); - var hr = manifestApp.GetAppUserModelId(out var tmpUserModelId); - UserModelId = AppxPackageHelper.CheckHRAndReturnOrThrow(hr, tmpUserModelId); + var hr = manifestApp->GetAppUserModelId(out var tmpUserModelIdPtr); + UserModelId = ComFreeHelper.GetStringAndFree(hr, tmpUserModelIdPtr); - hr = manifestApp.GetAppUserModelId(out var tmpUniqueIdentifier); - UniqueIdentifier = AppxPackageHelper.CheckHRAndReturnOrThrow(hr, tmpUniqueIdentifier); + manifestApp->GetAppUserModelId(out var tmpUniqueIdentifierPtr); + UniqueIdentifier = ComFreeHelper.GetStringAndFree(hr, tmpUniqueIdentifierPtr); - hr = manifestApp.GetStringValue("DisplayName", out var tmpDisplayName); - DisplayName = AppxPackageHelper.CheckHRAndReturnOrThrow(hr, tmpDisplayName); + manifestApp->GetStringValue("DisplayName", out var tmpDisplayNamePtr); + DisplayName = ComFreeHelper.GetStringAndFree(hr, tmpDisplayNamePtr); - hr = manifestApp.GetStringValue("Description", out var tmpDescription); - Description = AppxPackageHelper.CheckHRAndReturnOrThrow(hr, tmpDescription); + manifestApp->GetStringValue("Description", out var tmpDescriptionPtr); + Description = ComFreeHelper.GetStringAndFree(hr, tmpDescriptionPtr); - hr = manifestApp.GetStringValue("BackgroundColor", out var tmpBackgroundColor); - BackgroundColor = AppxPackageHelper.CheckHRAndReturnOrThrow(hr, tmpBackgroundColor); + manifestApp->GetStringValue("BackgroundColor", out var tmpBackgroundColorPtr); + BackgroundColor = ComFreeHelper.GetStringAndFree(hr, tmpBackgroundColorPtr); - hr = manifestApp.GetStringValue("EntryPoint", out var tmpEntryPoint); - EntryPoint = AppxPackageHelper.CheckHRAndReturnOrThrow(hr, tmpEntryPoint); + manifestApp->GetStringValue("EntryPoint", out var tmpEntryPointPtr); + EntryPoint = ComFreeHelper.GetStringAndFree(hr, tmpEntryPointPtr); Package = package ?? throw new ArgumentNullException(nameof(package)); @@ -166,7 +169,7 @@ public class UWPApplication : IProgram return false; } - internal string ResourceFromPri(string packageFullName, string resourceReference) + internal unsafe string ResourceFromPri(string packageFullName, string resourceReference) { const string prefix = "ms-resource:"; @@ -200,30 +203,8 @@ public class UWPApplication : IProgram parsedFallback = prefix + "///" + key; } - var outBuffer = new StringBuilder(128); - var source = $"@{{{packageFullName}? {parsed}}}"; - var capacity = (uint)outBuffer.Capacity; - var hResult = SHLoadIndirectString(source, outBuffer, capacity, IntPtr.Zero); - if (hResult != HRESULT.S_OK) + if (string.IsNullOrEmpty(parsedFallback)) { - if (!string.IsNullOrEmpty(parsedFallback)) - { - var sourceFallback = $"@{{{packageFullName}? {parsedFallback}}}"; - hResult = SHLoadIndirectString(sourceFallback, outBuffer, capacity, IntPtr.Zero); - if (hResult == HRESULT.S_OK) - { - var loaded = outBuffer.ToString(); - if (!string.IsNullOrEmpty(loaded)) - { - return loaded; - } - else - { - return string.Empty; - } - } - } - // https://github.com/Wox-launcher/Wox/issues/964 // known hresult 2147942522: // 'Microsoft Corporation' violates pattern constraint of '\bms-resource:.{1,256}'. @@ -232,17 +213,40 @@ public class UWPApplication : IProgram // Microsoft.BingFoodAndDrink_3.0.4.336_x64__8wekyb3d8bbwe: ms-resource:AppDescription return string.Empty; } - else + + var capacity = 1024U; + PWSTR outBuffer = new PWSTR((char*)(void*)Marshal.AllocHGlobal((int)capacity * sizeof(char))); + var source = $"@{{{packageFullName}? {parsed}}}"; + void* reserved = null; + + try { + PInvoke.SHLoadIndirectString(source, outBuffer, capacity, ref reserved).ThrowOnFailure(); + var loaded = outBuffer.ToString(); - if (!string.IsNullOrEmpty(loaded)) + return string.IsNullOrEmpty(loaded) ? string.Empty : loaded; + } + catch (Exception) + { + try { - return loaded; + var sourceFallback = $"@{{{packageFullName}?{parsedFallback}}}"; + PInvoke.SHLoadIndirectString(sourceFallback, outBuffer, capacity, ref reserved).ThrowOnFailure(); + var loaded = outBuffer.ToString(); + return string.IsNullOrEmpty(loaded) ? string.Empty : loaded; } - else + catch (Exception) { + // ProgramLogger.Exception($"Unable to load resource {resourceReference} from {packageFullName}", new InvalidOperationException(), GetType(), packageFullName); return string.Empty; } + finally + { + } + } + finally + { + Marshal.FreeHGlobal((IntPtr)outBuffer.Value); } } else @@ -258,13 +262,12 @@ public class UWPApplication : IProgram { PackageVersion.Windows8, "SmallLogo" }, }; - internal string LogoUriFromManifest(IAppxManifestApplication app) + internal unsafe string LogoUriFromManifest(IAppxManifestApplication* app) { if (_logoKeyFromVersion.TryGetValue(Package.Version, out var key)) { - var hr = app.GetStringValue(key, out var logoUriFromApp); - _ = AppxPackageHelper.CheckHRAndReturnOrThrow(hr, logoUriFromApp); - return logoUriFromApp; + var hr = app->GetStringValue(key, out var logoUriFromAppPtr); + return ComFreeHelper.GetStringAndFree(hr, logoUriFromAppPtr); } else { @@ -349,7 +352,7 @@ public class UWPApplication : IProgram var prefix = path.Substring(0, end); var paths = new List { }; const int appIconSize = 36; - var targetSizes = new List { 16, 24, 30, 36, 44, 60, 72, 96, 128, 180, 256 }.AsParallel(); + var targetSizes = new List { 16, 24, 30, 36, 44, 60, 72, 96, 128, 180, 256 }; var pathFactorPairs = new Dictionary(); foreach (var factor in targetSizes) diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/Win32Program.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/Win32Program.cs index 11c9be6be5..210febc549 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/Win32Program.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/Win32Program.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel.Design; using System.Diagnostics; @@ -841,18 +842,69 @@ public class Win32Program : IProgram var disabledProgramsList = settings.DisabledProgramSources; // Get all paths but exclude all normal .Executables - paths.UnionWith(sources - .AsParallel() - .SelectMany(source => source.IsEnabled ? source.GetPaths() : Enumerable.Empty()) - .Where(programPath => disabledProgramsList.All(x => x.UniqueIdentifier != programPath)) - .Where(path => !ExecutableApplicationExtensions.Contains(Extension(path)))); - runCommandPaths.UnionWith(runCommandSources - .AsParallel() - .SelectMany(source => source.IsEnabled ? source.GetPaths() : Enumerable.Empty()) - .Where(programPath => disabledProgramsList.All(x => x.UniqueIdentifier != programPath))); + var pathBag = new ConcurrentBag(); - var programs = paths.AsParallel().Select(source => GetProgramFromPath(source)); - var runCommandPrograms = runCommandPaths.AsParallel().Select(source => GetRunCommandProgramFromPath(source)); + Parallel.ForEach(sources, source => + { + if (!source.IsEnabled) + { + return; + } + + foreach (var path in source.GetPaths()) + { + if (disabledProgramsList.All(x => x.UniqueIdentifier != path) && + !ExecutableApplicationExtensions.Contains(Extension(path))) + { + pathBag.Add(path); + } + } + }); + + paths.UnionWith(pathBag); + + var runCommandPathBag = new ConcurrentBag(); + + Parallel.ForEach(runCommandSources, source => + { + if (!source.IsEnabled) + { + return; + } + + foreach (var path in source.GetPaths()) + { + if (disabledProgramsList.All(x => x.UniqueIdentifier != path)) + { + runCommandPathBag.Add(path); + } + } + }); + + runCommandPaths.UnionWith(runCommandPathBag); + + var programsList = new ConcurrentBag(); + Parallel.ForEach(paths, source => + { + var program = GetProgramFromPath(source); + if (program != null) + { + programsList.Add(program); + } + }); + + var runCommandProgramsList = new ConcurrentBag(); + Parallel.ForEach(runCommandPaths, source => + { + var program = GetRunCommandProgramFromPath(source); + if (program != null) + { + runCommandProgramsList.Add(program); + } + }); + + var programs = programsList.ToList(); + var runCommandPrograms = runCommandProgramsList.ToList(); return DeduplicatePrograms(programs.Concat(runCommandPrograms).Where(program => program?.Valid == true)); } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/ListRepository`1.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/ListRepository`1.cs index 653663b7e1..74877bf841 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/ListRepository`1.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/ListRepository`1.cs @@ -69,11 +69,6 @@ public class ListRepository : IRepository, IEnumerable } } - public ParallelQuery AsParallel() - { - return _items.Values.AsParallel(); - } - public bool Contains(T item) { if (item is not null) diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/Win32ProgramRepository.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/Win32ProgramRepository.cs index 9592599b17..b8e2064048 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/Win32ProgramRepository.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/Win32ProgramRepository.cs @@ -6,8 +6,8 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; using System.IO; -using System.IO.Abstractions; using System.Threading.Tasks; using ManagedCommon; using Microsoft.CmdPal.Ext.Apps.Programs; @@ -15,11 +15,9 @@ using Win32Program = Microsoft.CmdPal.Ext.Apps.Programs.Win32Program; namespace Microsoft.CmdPal.Ext.Apps.Storage; +[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] internal sealed partial class Win32ProgramRepository : ListRepository, IProgramRepository { - private static readonly IFileSystem FileSystem = new FileSystem(); - private static readonly IPath Path = FileSystem.Path; - private const string LnkExtension = ".lnk"; private const string UrlExtension = ".url"; diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/ComFreeHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/ComFreeHelper.cs new file mode 100644 index 0000000000..5f11987f76 --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/ComFreeHelper.cs @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.System.Com; + +namespace Microsoft.CmdPal.Ext.Apps.Utils; + +public static class ComFreeHelper +{ + internal static unsafe string GetStringAndFree(HRESULT hr, PWSTR ptr) + { + hr.ThrowOnFailure(); + try + { + return ptr.ToString(); + } + finally + { + PInvoke.CoTaskMemFree(ptr); + } + } + + public static unsafe void ComObjectRelease(T* comPtr) + where T : unmanaged + { + if (comPtr != null) + { + ((IUnknown*)comPtr)->Release(); + } + } +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/Native.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/Native.cs deleted file mode 100644 index 37918160fe..0000000000 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/Native.cs +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.InteropServices; -using System.Text; - -namespace Microsoft.CmdPal.Ext.Apps.Utils; - -[SuppressMessage("Interoperability", "CA1401:P/Invokes should not be visible", Justification = "We want plugins to share this NativeMethods class, instead of each one creating its own.")] -public sealed class Native -{ - [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)] - public static extern int SHLoadIndirectString(string pszSource, StringBuilder pszOutBuf, int cchOutBuf, nint ppvReserved); - - [DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = true)] - public static extern int SHCreateItemFromParsingName([MarshalAs(UnmanagedType.LPWStr)] string path, nint pbc, ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out IShellItem shellItem); - - [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)] - public static extern HRESULT SHCreateStreamOnFileEx(string fileName, STGM grfMode, uint attributes, bool create, System.Runtime.InteropServices.ComTypes.IStream reserved, out System.Runtime.InteropServices.ComTypes.IStream stream); - - [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)] - public static extern HRESULT SHLoadIndirectString(string pszSource, StringBuilder pszOutBuf, uint cchOutBuf, nint ppvReserved); - - public enum HRESULT : uint - { - /// - /// Operation successful. - /// - S_OK = 0x00000000, - - /// - /// Operation successful. (negative condition/no operation) - /// - S_FALSE = 0x00000001, - - /// - /// Not implemented. - /// - E_NOTIMPL = 0x80004001, - - /// - /// No such interface supported. - /// - E_NOINTERFACE = 0x80004002, - - /// - /// Pointer that is not valid. - /// - E_POINTER = 0x80004003, - - /// - /// Operation aborted. - /// - E_ABORT = 0x80004004, - - /// - /// Unspecified failure. - /// - E_FAIL = 0x80004005, - - /// - /// Unexpected failure. - /// - E_UNEXPECTED = 0x8000FFFF, - - /// - /// General access denied error. - /// - E_ACCESSDENIED = 0x80070005, - - /// - /// Handle that is not valid. - /// - E_HANDLE = 0x80070006, - - /// - /// Failed to allocate necessary memory. - /// - E_OUTOFMEMORY = 0x8007000E, - - /// - /// One or more arguments are not valid. - /// - E_INVALIDARG = 0x80070057, - - /// - /// The operation was canceled by the user. (Error source 7 means Win32.) - /// - /// - /// - E_CANCELLED = 0x800704C7, - } - - public static class ShellItemTypeConstants - { - /// - /// Guid for type IShellItem. - /// - public static readonly Guid ShellItemGuid = new("43826d1e-e718-42ee-bc55-a1e261c37bfe"); - - /// - /// Guid for type IShellItem2. - /// - public static readonly Guid ShellItem2Guid = new("7E9FB0D3-919F-4307-AB2E-9B1860310C93"); - } - - /// - /// The following are ShellItem DisplayName types. - /// - [Flags] - public enum SIGDN : uint - { - NORMALDISPLAY = 0, - PARENTRELATIVEPARSING = 0x80018001, - PARENTRELATIVEFORADDRESSBAR = 0x8001c001, - DESKTOPABSOLUTEPARSING = 0x80028000, - PARENTRELATIVEEDITING = 0x80031001, - DESKTOPABSOLUTEEDITING = 0x8004c000, - FILESYSPATH = 0x80058000, - URL = 0x80068000, - } - - [ComImport] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - [Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe")] - public interface IShellItem - { - void BindToHandler( - nint pbc, - [MarshalAs(UnmanagedType.LPStruct)] Guid bhid, - [MarshalAs(UnmanagedType.LPStruct)] Guid riid, - out nint ppv); - - void GetParent(out IShellItem ppsi); - - void GetDisplayName(SIGDN sigdnName, [MarshalAs(UnmanagedType.LPWStr)] out string ppszName); - - void GetAttributes(uint sfgaoMask, out uint psfgaoAttribs); - - void Compare(IShellItem psi, uint hint, out int piOrder); - } - - /// - /// see all STGM values - /// - [Flags] - public enum STGM : long - { - READ = 0x00000000L, - WRITE = 0x00000001L, - READWRITE = 0x00000002L, - CREATE = 0x00001000L, - } -} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/SafeComHandle.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/SafeComHandle.cs new file mode 100644 index 0000000000..86db1caf33 --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/SafeComHandle.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.InteropServices; + +namespace Microsoft.CmdPal.Ext.Apps.Utils; + +public partial class SafeComHandle : SafeHandle +{ + public SafeComHandle() + : base(IntPtr.Zero, ownsHandle: true) + { + } + + public SafeComHandle(IntPtr handle) + : base(IntPtr.Zero, ownsHandle: true) + { + SetHandle(handle); + } + + public override bool IsInvalid => handle == IntPtr.Zero; + + protected override bool ReleaseHandle() + { + var count = Marshal.Release(handle); + return true; + } +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/ShellLinkHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/ShellLinkHelper.cs index 1bb0e10580..543abf5dcf 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/ShellLinkHelper.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/ShellLinkHelper.cs @@ -3,124 +3,17 @@ // See the LICENSE file in the project root for more information. using System; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.ComTypes; using System.Text; using ManagedCommon; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.System.Com; +using Windows.Win32.UI.Shell; namespace Microsoft.CmdPal.Ext.Apps.Utils; public class ShellLinkHelper : IShellLinkHelper { - [Flags] - private enum SLGP_FLAGS - { - SLGP_SHORTPATH = 0x1, - SLGP_UNCPRIORITY = 0x2, - SLGP_RAWPATH = 0x4, - } - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Matching COM")] - private struct WIN32_FIND_DATAW - { - public uint dwFileAttributes; - public long ftCreationTime; - public long ftLastAccessTime; - public long ftLastWriteTime; - public uint nFileSizeHigh; - public uint nFileSizeLow; - public uint dwReserved0; - public uint dwReserved1; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] - public string cFileName; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)] - public string cAlternateFileName; - } - - [Flags] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Implements COM Interface")] - public enum SLR_FLAGS - { - SLR_NO_UI = 0x1, - SLR_ANY_MATCH = 0x2, - SLR_UPDATE = 0x4, - SLR_NOUPDATE = 0x8, - SLR_NOSEARCH = 0x10, - SLR_NOTRACK = 0x20, - SLR_NOLINKINFO = 0x40, - SLR_INVOKE_MSI = 0x80, - } - - // Reference : http://www.pinvoke.net/default.aspx/Interfaces.IShellLinkW - - // The IShellLink interface allows Shell links to be created, modified, and resolved - [ComImport] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - [Guid("000214F9-0000-0000-C000-000000000046")] - private interface IShellLinkW - { - /// Retrieves the path and file name of a Shell link object - void GetPath([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, ref WIN32_FIND_DATAW pfd, SLGP_FLAGS fFlags); - - /// Retrieves the list of item identifiers for a Shell link object - void GetIDList(out nint ppidl); - - /// Sets the pointer to an item identifier list (PIDL) for a Shell link object. - void SetIDList(nint pidl); - - /// Retrieves the description string for a Shell link object - void GetDescription([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, int cchMaxName); - - /// Sets the description for a Shell link object. The description can be any application-defined string - void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName); - - /// Retrieves the name of the working directory for a Shell link object - void GetWorkingDirectory([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath); - - /// Sets the name of the working directory for a Shell link object - void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir); - - /// Retrieves the command-line arguments associated with a Shell link object - void GetArguments([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath); - - /// Sets the command-line arguments for a Shell link object - void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs); - - /// Retrieves the hot key for a Shell link object - void GetHotkey(out short pwHotkey); - - /// Sets a hot key for a Shell link object - void SetHotkey(short wHotkey); - - /// Retrieves the show command for a Shell link object - void GetShowCmd(out int piShowCmd); - - /// Sets the show command for a Shell link object. The show command sets the initial show state of the window. - void SetShowCmd(int iShowCmd); - - /// Retrieves the location (path and index) of the icon for a Shell link object - void GetIconLocation([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, int cchIconPath, out int piIcon); - - /// Sets the location (path and index) of the icon for a Shell link object - void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon); - - /// Sets the relative path to the Shell link object - void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, int dwReserved); - - /// Attempts to find the target of a Shell link, even if it has been moved or renamed - void Resolve(ref nint hwnd, SLR_FLAGS fFlags); - - /// Sets the path and file name of a Shell link object - void SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile); - } - - [ComImport] - [Guid("00021401-0000-0000-C000-000000000046")] - private class ShellLink - { - } - // Contains the description of the app public string Description { get; set; } = string.Empty; @@ -130,60 +23,63 @@ public class ShellLinkHelper : IShellLinkHelper public bool HasArguments { get; set; } // Retrieve the target path using Shell Link - public string RetrieveTargetPath(string path) + public unsafe string RetrieveTargetPath(string path) { - var link = new ShellLink(); - const int STGM_READ = 0; - - try - { - ((IPersistFile)link).Load(path, STGM_READ); - } - catch (System.IO.FileNotFoundException ex) - { - Logger.LogError(ex.Message); - return string.Empty; - } - - var hwnd = default(nint); - ((IShellLinkW)link).Resolve(ref hwnd, 0); - + var target = string.Empty; const int MAX_PATH = 260; - var buffer = new StringBuilder(MAX_PATH); + IShellLinkW* link = null; - var data = default(WIN32_FIND_DATAW); - ((IShellLinkW)link).GetPath(buffer, buffer.Capacity, ref data, SLGP_FLAGS.SLGP_SHORTPATH); - var target = buffer.ToString(); + PInvoke.CoCreateInstance(typeof(ShellLink).GUID, null, CLSCTX.CLSCTX_INPROC_SERVER, out link).ThrowOnFailure(); + using var linkHandle = new SafeComHandle((IntPtr)link); + + const int STGMREAD = 0; + + IPersistFile* persistFile = null; + Guid iid = typeof(IPersistFile).GUID; + ((IUnknown*)link)->QueryInterface(&iid, (void**)&persistFile); + if (persistFile != null) + { + using var persistFileHandle = new SafeComHandle((IntPtr)persistFile); + try + { + persistFile->Load(path, STGMREAD); + } + catch (System.IO.FileNotFoundException) + { + // Log.Exception($"Failed to load {path}, {e.Message}", e, GetType()); + return string.Empty; + } + } + + var hwnd = HWND.Null; + const uint SLR_NO_UI = 0x1; + link->Resolve(hwnd, SLR_NO_UI); + + var buffer = stackalloc char[MAX_PATH]; + + var hr = link->GetPath((PWSTR)buffer, MAX_PATH, null, 0x1); + + target = hr.Succeeded ? new string(buffer) : string.Empty; // To set the app description if (!string.IsNullOrEmpty(target)) { - buffer = new StringBuilder(MAX_PATH); - try - { - ((IShellLinkW)link).GetDescription(buffer, MAX_PATH); - Description = buffer.ToString(); - } - catch (Exception ex) - { - Logger.LogError(ex.Message); - Description = string.Empty; - } + var descBuffer = stackalloc char[MAX_PATH]; + var desHr = link->GetDescription(descBuffer, MAX_PATH); + Description = desHr.Succeeded ? new string(descBuffer) : string.Empty; - var argumentBuffer = new StringBuilder(MAX_PATH); - ((IShellLinkW)link).GetArguments(argumentBuffer, argumentBuffer.Capacity); - Arguments = argumentBuffer.ToString(); + var argsBuffer = stackalloc char[MAX_PATH]; + var argHr = link->GetArguments(argsBuffer, MAX_PATH); + + Arguments = argHr.Succeeded ? new string(argsBuffer) : string.Empty; // Set variable to true if the program takes in any arguments - if (argumentBuffer.Length != 0) + if (Arguments.Length != 0) { HasArguments = true; } } - // To release unmanaged memory - Marshal.ReleaseComObject(link); - return target; } } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/ShellLocalization.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/ShellLocalization.cs index 0a3337e0a8..87e152b7e0 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/ShellLocalization.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/ShellLocalization.cs @@ -4,7 +4,8 @@ using System; using System.Collections.Concurrent; using System.IO; -using static Microsoft.CmdPal.Ext.Apps.Utils.Native; +using Windows.Win32; +using Windows.Win32.UI.Shell; namespace Microsoft.CmdPal.Ext.Apps.Utils; @@ -23,7 +24,7 @@ public class ShellLocalization /// /// Path to the shell item (e. g. shortcut 'File Explorer.lnk'). /// The localized name as string or . - public string GetLocalizedName(string path) + public unsafe string GetLocalizedName(string path) { var lowerInvariantPath = path.ToLowerInvariant(); @@ -33,18 +34,29 @@ public class ShellLocalization return value; } - var shellItemType = ShellItemTypeConstants.ShellItemGuid; - var retCode = SHCreateItemFromParsingName(path, nint.Zero, ref shellItemType, out var shellItem); - if (retCode != 0) + void* shellItemPtrVoid = null; + try + { + var retCode = PInvoke.SHCreateItemFromParsingName(path, null, typeof(IShellItem).GUID, out shellItemPtrVoid).ThrowOnFailure(); + using var shellItemHandle = new SafeComHandle((IntPtr)shellItemPtrVoid); + IShellItem* shellItemPtr = (IShellItem*)shellItemPtrVoid; + + var hr = shellItemPtr->GetDisplayName(SIGDN.SIGDN_NORMALDISPLAY, out var filenamePtr); + + var filename = ComFreeHelper.GetStringAndFree(hr, filenamePtr); + + if (filename == null) + { + return string.Empty; + } + + _ = _localizationCache.TryAdd(lowerInvariantPath, filename); + return filename; + } + catch (Exception) { return string.Empty; } - - shellItem.GetDisplayName(SIGDN.NORMALDISPLAY, out var filename); - - _ = _localizationCache.TryAdd(lowerInvariantPath, filename); - - return filename; } /// From 88e4ba670c2da8f3d9b4558a44702fdaca140407 Mon Sep 17 00:00:00 2001 From: leileizhang Date: Wed, 4 Jun 2025 23:36:50 +0800 Subject: [PATCH 037/117] [Fuzzing] Use project reference for PowerRenameLib to fix Debug build link error (#39903) ## Summary of the Pull Request This change replaces the manual .lib reference with a proper project reference to PowerRenameLib in the PowerRename.FuzzingTest project. Previously, the project used a hardcoded path to link against PowerRenameLib.lib, which caused build errors in Debug mode when the .lib file had not yet been generated or the output path was incorrect. ## PR Checklist - [ ] **Closes:** #xxx - [ ] **Communication:** I've discussed this with core contributors already. If work hasn't been agreed, this work might be rejected - [ ] **Tests:** Added/updated and all pass - [ ] **Localization:** All end user facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx ## Detailed Description of the Pull Request / Additional comments ## Validation Steps Performed --- .../PowerRename.FuzzingTest/PowerRename.FuzzingTest.vcxproj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/modules/powerrename/PowerRename.FuzzingTest/PowerRename.FuzzingTest.vcxproj b/src/modules/powerrename/PowerRename.FuzzingTest/PowerRename.FuzzingTest.vcxproj index 6dc9ddcdb2..63ba1028a4 100644 --- a/src/modules/powerrename/PowerRename.FuzzingTest/PowerRename.FuzzingTest.vcxproj +++ b/src/modules/powerrename/PowerRename.FuzzingTest/PowerRename.FuzzingTest.vcxproj @@ -72,7 +72,6 @@ Console - $(OutDir)\..\..\WinUI3Apps\PowerRenameLib.lib;comctl32.lib;pathcch.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;Pathcch.lib;%(AdditionalDependencies) @@ -85,6 +84,9 @@ {51920f1f-c28c-4adf-8660-4238766796c2} + + {51920f1f-c28c-4adf-8660-4238766796c2} + {6955446d-23f7-4023-9bb3-8657f904af99} From df8ace3ab64c84a460af5fe6f5a773f201c582fe Mon Sep 17 00:00:00 2001 From: Yu Leng <42196638+moooyo@users.noreply.github.com> Date: Thu, 5 Jun 2025 15:02:03 +0800 Subject: [PATCH 038/117] Release ptr which get from CoCreateInstance to prevent memory leak (#39898) ## Summary of the Pull Request We need to release it manually. ## PR Checklist - [ ] **Closes:** #xxx - [ ] **Communication:** I've discussed this with core contributors already. If work hasn't been agreed, this work might be rejected - [ ] **Tests:** Added/updated and all pass - [ ] **Localization:** All end user facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx ## Detailed Description of the Pull Request / Additional comments ## Validation Steps Performed --------- Co-authored-by: Yu Leng --- .../Indexer/DataSourceManager.cs | 5 +++++ .../Indexer/Utils/QueryStringBuilder.cs | 8 +++++++- .../Helpers/VirtualDesktopHelper.cs | 10 +++++++++- .../Commands/LaunchProfileAsAdminCommand.cs | 11 ++++++++++- .../Commands/LaunchProfileCommand.cs | 11 ++++++++++- .../Helpers/NativeHelpers.cs | 8 ++------ 6 files changed, 43 insertions(+), 10 deletions(-) diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/DataSourceManager.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/DataSourceManager.cs index 01c6d84357..5ddaf7b02d 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/DataSourceManager.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/DataSourceManager.cs @@ -53,6 +53,11 @@ internal static class DataSourceManager _dataSource.Initialize(); + if (dataSourceObjPtr != IntPtr.Zero) + { + Marshal.Release(dataSourceObjPtr); + } + return true; } } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/Utils/QueryStringBuilder.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/Utils/QueryStringBuilder.cs index e025492de6..1e93fe7f80 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/Utils/QueryStringBuilder.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/Utils/QueryStringBuilder.cs @@ -30,8 +30,9 @@ internal sealed partial class QueryStringBuilder if (queryHelper == null) { ComWrappers cw = new StrategyBasedComWrappers(); + var searchManagerPtr = IntPtr.Zero; - var hr = NativeMethods.CoCreateInstance(ref Unsafe.AsRef(in NativeHelpers.CsWin32GUID.CLSIDSearchManager), IntPtr.Zero, NativeHelpers.CLSCTXINPROCALL, ref Unsafe.AsRef(in NativeHelpers.CsWin32GUID.IIDISearchManager), out var searchManagerPtr); + var hr = NativeMethods.CoCreateInstance(ref Unsafe.AsRef(in NativeHelpers.CsWin32GUID.CLSIDSearchManager), IntPtr.Zero, NativeHelpers.CLSCTXINPROCALL, ref Unsafe.AsRef(in NativeHelpers.CsWin32GUID.IIDISearchManager), out searchManagerPtr); if (hr != 0) { throw new ArgumentException($"Failed to create SearchManager instance. HR: 0x{hr:X}"); @@ -51,6 +52,11 @@ internal sealed partial class QueryStringBuilder throw new ArgumentException($"Failed to get catalog manager for {SystemIndex}"); } + if (searchManagerPtr != IntPtr.Zero) + { + Marshal.Release(searchManagerPtr); + } + queryHelper = catalogManager.GetQueryHelper(); if (queryHelper == null) { diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/VirtualDesktopHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/VirtualDesktopHelper.cs index 112e2361de..b035040bde 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/VirtualDesktopHelper.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/VirtualDesktopHelper.cs @@ -71,10 +71,11 @@ public class VirtualDesktopHelper public VirtualDesktopHelper(bool desktopListUpdate = false) { var cw = new StrategyBasedComWrappers(); + var virtualDesktopManagerPtr = IntPtr.Zero; try { - var hr = NativeMethods.CoCreateInstance(ref this.iVirtualDesktopManagerCLSID, nint.Zero, CLSCTXINPROCALL, ref iVirtualDesktopManagerIID, out var virtualDesktopManagerPtr); + var hr = NativeMethods.CoCreateInstance(ref this.iVirtualDesktopManagerCLSID, nint.Zero, CLSCTXINPROCALL, ref iVirtualDesktopManagerIID, out virtualDesktopManagerPtr); if (hr != 0) { throw new ArgumentException($"Failed to create IVirtualDesktopManager instance. HR: 0x{hr:X}"); @@ -87,6 +88,13 @@ public class VirtualDesktopHelper ExtensionHost.LogMessage(new LogMessage() { Message = $"Initialization of failed: An exception was thrown when creating the instance of COM interface . {ex} " }); return; } + finally + { + if (virtualDesktopManagerPtr != IntPtr.Zero) + { + Marshal.Release(virtualDesktopManagerPtr); + } + } _isWindowsEleven = OSVersionHelper.IsWindows11(); _desktopListAutoUpdate = desktopListUpdate; diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Commands/LaunchProfileAsAdminCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Commands/LaunchProfileAsAdminCommand.cs index 4a5170250a..53c8e59cd4 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Commands/LaunchProfileAsAdminCommand.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Commands/LaunchProfileAsAdminCommand.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; using ManagedCommon; @@ -63,8 +64,9 @@ internal sealed partial class LaunchProfileAsAdminCommand : InvokableCommand private void Launch(string id, string profile) { ComWrappers cw = new StrategyBasedComWrappers(); + var appManagerPtr = IntPtr.Zero; - var hr = NativeHelpers.CoCreateInstance(ref NativeHelpers.ApplicationActivationManagerCLSID, IntPtr.Zero, NativeHelpers.CLSCTXINPROCALL, ref NativeHelpers.ApplicationActivationManagerIID, out var appManagerPtr); + var hr = NativeHelpers.CoCreateInstance(ref Unsafe.AsRef(in NativeHelpers.ApplicationActivationManagerCLSID), IntPtr.Zero, NativeHelpers.CLSCTXINPROCALL, ref Unsafe.AsRef(in NativeHelpers.ApplicationActivationManagerIID), out appManagerPtr); if (hr != 0) { @@ -95,6 +97,13 @@ internal sealed partial class LaunchProfileAsAdminCommand : InvokableCommand // _context.API.ShowMsg(name, message, string.Empty); Logger.LogError($"Failed to open Windows Terminal: {ex.Message}"); } + finally + { + if (appManagerPtr != IntPtr.Zero) + { + Marshal.Release(appManagerPtr); + } + } } #pragma warning restore IDE0059, CS0168 diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Commands/LaunchProfileCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Commands/LaunchProfileCommand.cs index 5f9d4ea00d..c7a1dec014 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Commands/LaunchProfileCommand.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Commands/LaunchProfileCommand.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; using ManagedCommon; @@ -33,8 +34,9 @@ internal sealed partial class LaunchProfileCommand : InvokableCommand private void Launch(string id, string profile) { ComWrappers cw = new StrategyBasedComWrappers(); + var appManagerPtr = IntPtr.Zero; - var hr = NativeHelpers.CoCreateInstance(ref NativeHelpers.ApplicationActivationManagerCLSID, IntPtr.Zero, NativeHelpers.CLSCTXINPROCALL, ref NativeHelpers.ApplicationActivationManagerIID, out var appManagerPtr); + var hr = NativeHelpers.CoCreateInstance(ref Unsafe.AsRef(in NativeHelpers.ApplicationActivationManagerCLSID), IntPtr.Zero, NativeHelpers.CLSCTXINPROCALL, ref Unsafe.AsRef(in NativeHelpers.ApplicationActivationManagerIID), out appManagerPtr); if (hr != 0) { throw new ArgumentException($"Failed to create IApplicationActivationManager instance. HR: 0x{hr:X}"); @@ -64,6 +66,13 @@ internal sealed partial class LaunchProfileCommand : InvokableCommand // _context.API.ShowMsg(name, message, string.Empty); Logger.LogError($"Failed to open Windows Terminal: {ex.Message}"); } + finally + { + if (appManagerPtr != IntPtr.Zero) + { + Marshal.Release(appManagerPtr); + } + } } #pragma warning restore IDE0059, CS0168 diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/NativeHelpers.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/NativeHelpers.cs index e431d15a1b..254a8c3dcf 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/NativeHelpers.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/NativeHelpers.cs @@ -23,10 +23,6 @@ public sealed partial class NativeHelpers ref Guid riid, out IntPtr rReturnedComObject); -#pragma warning disable CA2211 // Non-constant fields should not be visible -#pragma warning disable SA1401 // Fields should be private - public static Guid ApplicationActivationManagerCLSID = new Guid("45BA127D-10A8-46EA-8AB7-56EA9078943C"); - public static Guid ApplicationActivationManagerIID = new Guid("2e941141-7f97-4756-ba1d-9decde894a3d"); -#pragma warning restore SA1401 // Fields should be private -#pragma warning restore CA2211 // Non-constant fields should not be visible + public static readonly Guid ApplicationActivationManagerCLSID = new Guid("45BA127D-10A8-46EA-8AB7-56EA9078943C"); + public static readonly Guid ApplicationActivationManagerIID = new Guid("2e941141-7f97-4756-ba1d-9decde894a3d"); } From dd2e7d17f956750b190a17f3987ea8b8f21d616f Mon Sep 17 00:00:00 2001 From: Yu Leng <42196638+moooyo@users.noreply.github.com> Date: Thu, 5 Jun 2025 15:23:16 +0800 Subject: [PATCH 039/117] [cmdpal] Re-enable Clipboard History extention (#39800) ## Summary of the Pull Request Due to some windows sdk bugs, we can not use those API in main thread. So, create a separate thread for clipboard. history: ![image](https://github.com/user-attachments/assets/4da5a4eb-5d9d-475c-ab13-a2d585d2fffc) success to paste to chat: ![image](https://github.com/user-attachments/assets/3fef43e5-4fc5-492c-b81e-599a9746d413) ![image](https://github.com/user-attachments/assets/1f4232bb-de76-40e5-96dd-43beb0ca8423) ## PR Checklist - [x] **Closes:** #38344 - [ ] **Communication:** I've discussed this with core contributors already. If work hasn't been agreed, this work might be rejected - [x] **Tests:** Added/updated and all pass - [x] **Localization:** All end user facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx ## Detailed Description of the Pull Request / Additional comments ## Validation Steps Performed --------- Co-authored-by: Yu Leng (from Dev Box) --- .../cmdpal/Microsoft.CmdPal.UI/App.xaml.cs | 4 +- .../Helpers/ClipboardHelper.cs | 48 +++++++----- .../Helpers/ClipboardThreadScheduler.cs | 74 +++++++++++++++++++ .../Helpers/NativeMethods.cs | 8 +- 4 files changed, 112 insertions(+), 22 deletions(-) create mode 100644 src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/Helpers/ClipboardThreadScheduler.cs diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/App.xaml.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/App.xaml.cs index ba4a298665..4917dc5f0e 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/App.xaml.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/App.xaml.cs @@ -8,6 +8,7 @@ using Microsoft.CmdPal.Common.Services; using Microsoft.CmdPal.Ext.Apps; using Microsoft.CmdPal.Ext.Bookmarks; using Microsoft.CmdPal.Ext.Calc; +using Microsoft.CmdPal.Ext.ClipboardHistory; using Microsoft.CmdPal.Ext.Indexer; using Microsoft.CmdPal.Ext.Registry; using Microsoft.CmdPal.Ext.Shell; @@ -100,8 +101,7 @@ public partial class App : Application services.AddSingleton(); services.AddSingleton(); - // TODO GH #527 re-enable the clipboard commands - // services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/Helpers/ClipboardHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/Helpers/ClipboardHelper.cs index bbfec3c491..100d23e9f0 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/Helpers/ClipboardHelper.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/Helpers/ClipboardHelper.cs @@ -30,6 +30,8 @@ internal static class ClipboardHelper (StandardDataFormats.Bitmap, ClipboardFormat.Image), ]; + private static readonly ClipboardThreadQueue ClipboardThreadQueue = new ClipboardThreadQueue(); + internal static async Task GetAvailableClipboardFormatsAsync(DataPackageView clipboardData) { var availableClipboardFormats = DataFormats.Aggregate( @@ -58,9 +60,12 @@ internal static class ClipboardHelper try { // Clipboard.SetContentWithOptions(output, null); - Clipboard.SetContent(output); - Flush(); - ExtensionHost.LogMessage(new LogMessage() { Message = "Copied text to clipboard" }); + ClipboardThreadQueue.EnqueueTask(() => + { + Clipboard.SetContent(output); + Flush(); + ExtensionHost.LogMessage(new LogMessage() { Message = "Copied text to clipboard" }); + }); } catch (COMException ex) { @@ -74,27 +79,32 @@ internal static class ClipboardHelper // TODO(stefan): For some reason Flush() fails from time to time when directly activated via hotkey. // Calling inside a loop makes it work. // Exception is: The operation is not permitted because the calling application is not the owner of the data on the clipboard. - const int maxAttempts = 5; - for (var i = 1; i <= maxAttempts; i++) + ClipboardThreadQueue.EnqueueTask(() => { - try + const int maxAttempts = 5; + + for (var i = 1; i <= maxAttempts; i++) { - Task.Run(Clipboard.Flush).Wait(); - return true; - } - catch (Exception ex) - { - if (i == maxAttempts) + try { - ExtensionHost.LogMessage(new LogMessage() + Task.Run(Clipboard.Flush).Wait(); + return; + } + catch (Exception ex) + { + if (i == maxAttempts) { - Message = $"{nameof(Clipboard)}.{nameof(Flush)}() failed: {ex}", - }); + ExtensionHost.LogMessage(new LogMessage() + { + Message = $"{nameof(Clipboard)}.{nameof(Flush)}() failed: {ex}", + }); + } } } - } + }); - return false; + // We cannot get the real result of the Flush() call here, as it is executed in a different thread. + return true; } private static async Task FlushAsync() => await Task.Run(Flush); @@ -105,7 +115,7 @@ internal static class ClipboardHelper DataPackage output = new(); output.SetStorageItems([storageFile]); - Clipboard.SetContent(output); + ClipboardThreadQueue.EnqueueTask(() => Clipboard.SetContent(output)); await FlushAsync(); } @@ -118,7 +128,7 @@ internal static class ClipboardHelper { DataPackage output = new(); output.SetBitmap(image); - Clipboard.SetContentWithOptions(output, null); + ClipboardThreadQueue.EnqueueTask(() => Clipboard.SetContentWithOptions(output, null)); Flush(); } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/Helpers/ClipboardThreadScheduler.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/Helpers/ClipboardThreadScheduler.cs new file mode 100644 index 0000000000..0f36f66453 --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/Helpers/ClipboardThreadScheduler.cs @@ -0,0 +1,74 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Concurrent; +using System.Threading; +using Microsoft.CommandPalette.Extensions.Toolkit; + +namespace Microsoft.CmdPal.Ext.ClipboardHistory.Helpers; + +public partial class ClipboardThreadQueue : IDisposable +{ + private readonly Thread _thread; + private readonly ConcurrentQueue _taskQueue = new ConcurrentQueue(); + private readonly AutoResetEvent _taskAvailable = new AutoResetEvent(false); + private readonly CancellationTokenSource cancellationToken = new CancellationTokenSource(); + + public ClipboardThreadQueue() + { + _thread = new Thread(() => + { + var hr = NativeMethods.CoInitialize(IntPtr.Zero); + if (hr != 0) + { + ExtensionHost.LogMessage($"CoInitialize failed with HRESULT: {hr}"); + } + + while (true) + { + _taskAvailable.WaitOne(); + + if (cancellationToken.IsCancellationRequested) + { + break; + } + + while (_taskQueue.TryDequeue(out var task)) + { + try + { + task(); + } + catch (Exception ex) + { + ExtensionHost.LogMessage($"Error executing task in ClipboardThreadQueue: {ex.Message}"); + } + } + } + + NativeMethods.CoUninitialize(); + }); + + _thread.SetApartmentState(ApartmentState.STA); + _thread.IsBackground = true; + _thread.Start(); + } + + public void EnqueueTask(Action task) + { + _taskQueue.Enqueue(task); + _taskAvailable.Set(); + } + + public void Dispose() + { + cancellationToken.Cancel(); + _taskAvailable.Set(); + _thread.Join(); // Wait for the thread to finish processing tasks + + _taskAvailable.Dispose(); + GC.SuppressFinalize(this); + } +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/Helpers/NativeMethods.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/Helpers/NativeMethods.cs index f4b6089229..1e0a46f030 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/Helpers/NativeMethods.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/Helpers/NativeMethods.cs @@ -8,7 +8,7 @@ using Windows.Foundation; namespace Microsoft.CmdPal.Ext.ClipboardHistory.Helpers; -internal static class NativeMethods +public static partial class NativeMethods { [StructLayout(LayoutKind.Sequential)] [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Matching Native Structure")] @@ -98,4 +98,10 @@ internal static class NativeMethods [DllImport("user32.dll")] internal static extern bool GetCursorPos(out PointInter lpPoint); + + [LibraryImport("ole32.dll")] + internal static partial int CoInitialize(IntPtr pvReserved); + + [LibraryImport("ole32.dll")] + internal static partial void CoUninitialize(); } From 78b29c5b667e5b84384d0ba2f310815bf96f8e0f Mon Sep 17 00:00:00 2001 From: Michael Jolley Date: Thu, 5 Jun 2025 08:52:25 -0500 Subject: [PATCH 040/117] CmdPal: Display correct context items for run fallback (#39521) Corrected the Run fallback handler to provide the same context items as it does on the run page. ![image](https://github.com/user-attachments/assets/04a444b4-dc9f-4b63-8231-8f47f593b60f) **Closes:** #39092 --- .../ext/Microsoft.CmdPal.Ext.Shell/FallbackExecuteItem.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/FallbackExecuteItem.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/FallbackExecuteItem.cs index d1bdb0e7be..d3bc979419 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/FallbackExecuteItem.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/FallbackExecuteItem.cs @@ -12,10 +12,12 @@ namespace Microsoft.CmdPal.Ext.Shell; internal sealed partial class FallbackExecuteItem : FallbackCommandItem { private readonly ExecuteItem _executeItem; + private readonly SettingsManager _settings; public FallbackExecuteItem(SettingsManager settings) : base(new ExecuteItem(string.Empty, settings), Resources.shell_command_display_title) { + _settings = settings; _executeItem = (ExecuteItem)this.Command!; Title = string.Empty; _executeItem.Name = string.Empty; @@ -28,5 +30,9 @@ internal sealed partial class FallbackExecuteItem : FallbackCommandItem _executeItem.Cmd = query; _executeItem.Name = string.IsNullOrEmpty(query) ? string.Empty : Properties.Resources.generic_run_command; Title = query; + MoreCommands = [ + new CommandContextItem(new ExecuteItem(query, _settings, RunAsType.Administrator)), + new CommandContextItem(new ExecuteItem(query, _settings, RunAsType.OtherUser)), + ]; } } From bf9217ec244397b854543790edebd6d272ebd99f Mon Sep 17 00:00:00 2001 From: Michael Jolley Date: Thu, 5 Jun 2025 08:56:13 -0500 Subject: [PATCH 041/117] CmdPal: Implement IDetailsCommand in details (#39911) Implemented IDetailsCommands in details. This will close #38339. This works very similar to Tags in that it is a list of commands. This was done to allow for styling without the 12 spacing of the ItemsRepeater and looks like you'd find in the OS-inbox like: ![image](https://github.com/user-attachments/assets/97ee1952-13bb-4c8b-a074-0347b04e0c2c) ![image](https://github.com/user-attachments/assets/8f6f1f72-4ea0-441d-893e-ae26aabdc922) Also added to our sample extension: ![image](https://github.com/user-attachments/assets/ab3ce521-3479-448f-b4d6-9dfd09feb24f) --- .../DetailsCommandsViewModel.cs | 42 +++++++++++++++++++ .../DetailsViewModel.cs | 1 + .../Converters/DetailsDataTemplateSelector.cs | 3 ++ .../Microsoft.CmdPal.UI/Pages/ShellPage.xaml | 34 +++++++++++++++ .../Pages/ShellPage.xaml.cs | 8 ++++ .../Pages/SampleListPageWithDetails.cs | 20 +++++++++ .../{DetailsCommand.cs => DetailsCommands.cs} | 4 +- .../Microsoft.CommandPalette.Extensions.idl | 4 +- 8 files changed, 112 insertions(+), 4 deletions(-) create mode 100644 src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/DetailsCommandsViewModel.cs rename src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/{DetailsCommand.cs => DetailsCommands.cs} (70%) diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/DetailsCommandsViewModel.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/DetailsCommandsViewModel.cs new file mode 100644 index 0000000000..316209a296 --- /dev/null +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/DetailsCommandsViewModel.cs @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CmdPal.UI.ViewModels.Models; +using Microsoft.CommandPalette.Extensions; + +namespace Microsoft.CmdPal.UI.ViewModels; + +public partial class DetailsCommandsViewModel( + IDetailsElement _detailsElement, + WeakReference context) : DetailsElementViewModel(_detailsElement, context) +{ + public List Commands { get; private set; } = []; + + public bool HasCommands => Commands.Count > 0; + + private readonly ExtensionObject _dataModel = + new(_detailsElement.Data as IDetailsCommands); + + public override void InitializeProperties() + { + base.InitializeProperties(); + var model = _dataModel.Unsafe; + if (model == null) + { + return; + } + + Commands = model + .Commands? + .Select(c => + { + var vm = new CommandViewModel(c, PageContext); + vm.InitializeProperties(); + return vm; + }) + .ToList() ?? []; + UpdateProperty(nameof(HasCommands)); + UpdateProperty(nameof(Commands)); + } +} diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/DetailsViewModel.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/DetailsViewModel.cs index 0d5233fa41..893a94f86e 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/DetailsViewModel.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/DetailsViewModel.cs @@ -49,6 +49,7 @@ public partial class DetailsViewModel(IDetails _details, WeakReference new DetailsSeparatorViewModel(element, this.PageContext), IDetailsLink => new DetailsLinkViewModel(element, this.PageContext), + IDetailsCommands => new DetailsCommandsViewModel(element, this.PageContext), IDetailsTags => new DetailsTagsViewModel(element, this.PageContext), _ => null, }; diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Converters/DetailsDataTemplateSelector.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/Converters/DetailsDataTemplateSelector.cs index 328e0ca01e..bff62bb97f 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Converters/DetailsDataTemplateSelector.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Converters/DetailsDataTemplateSelector.cs @@ -18,6 +18,8 @@ public partial class DetailsDataTemplateSelector : DataTemplateSelector public DataTemplate? TagTemplate { get; set; } + public DataTemplate? CommandTemplate { get; set; } + protected override DataTemplate? SelectTemplateCore(object item) { if (item is DetailsElementViewModel element) @@ -27,6 +29,7 @@ public partial class DetailsDataTemplateSelector : DataTemplateSelector { DetailsSeparatorViewModel => SeparatorTemplate, DetailsLinkViewModel => LinkTemplate, + DetailsCommandsViewModel => CommandTemplate, DetailsTagsViewModel => TagTemplate, _ => null, }; diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Pages/ShellPage.xaml b/src/modules/cmdpal/Microsoft.CmdPal.UI/Pages/ShellPage.xaml index f97334466e..2dd0c5b6c4 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Pages/ShellPage.xaml +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Pages/ShellPage.xaml @@ -29,6 +29,7 @@ @@ -50,6 +51,27 @@ ToolTipService.ToolTip="{x:Bind ToolTip, Mode=OneWay}" /> + + + + + + + + + + + + (new(commandViewModel.Model)); + } + } } diff --git a/src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleListPageWithDetails.cs b/src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleListPageWithDetails.cs index 9495a7f6bf..018c11720c 100644 --- a/src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleListPageWithDetails.cs +++ b/src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleListPageWithDetails.cs @@ -5,6 +5,7 @@ using System; using Microsoft.CommandPalette.Extensions; using Microsoft.CommandPalette.Extensions.Toolkit; +using Microsoft.UI.Xaml; namespace SamplePagesExtension; @@ -129,6 +130,25 @@ internal sealed partial class SampleListPageWithDetails : ListPage ], }, }, + new DetailsElement() + { + Key = "Commands", + Data = new DetailsCommands() + { + Commands = [ + new ToastCommand("Hey! You clicked it!", MessageState.Success) + { + Name = "Do something amazing", + Icon = new("\uE945"), + }, + new ToastCommand("I warned you!", MessageState.Error) + { + Name = "Don't click me", + Icon = new("\uEA39"), + }, + ], + }, + }, ], }, } diff --git a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/DetailsCommand.cs b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/DetailsCommands.cs similarity index 70% rename from src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/DetailsCommand.cs rename to src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/DetailsCommands.cs index 0d08458ebf..32b11faad4 100644 --- a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/DetailsCommand.cs +++ b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/DetailsCommands.cs @@ -4,7 +4,7 @@ namespace Microsoft.CommandPalette.Extensions.Toolkit; -public partial class DetailsCommand : IDetailsCommand +public partial class DetailsCommands : IDetailsCommands { - public ICommand? Command { get; set; } + public ICommand[]? Commands { get; set; } } diff --git a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/Microsoft.CommandPalette.Extensions.idl b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/Microsoft.CommandPalette.Extensions.idl index df4b442cdf..3105b0647d 100644 --- a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/Microsoft.CommandPalette.Extensions.idl +++ b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/Microsoft.CommandPalette.Extensions.idl @@ -182,8 +182,8 @@ namespace Microsoft.CommandPalette.Extensions String Text { get; }; } [contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)] - interface IDetailsCommand requires IDetailsData { - ICommand Command { get; }; + interface IDetailsCommands requires IDetailsData { + ICommand[] Commands { get; }; } [uuid("58070392-02bb-4e89-9beb-47ceb8c3d741")] [contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)] From 2aad949c9ecc817a6abcd96cf8ac76e08a9e3400 Mon Sep 17 00:00:00 2001 From: Clint Rutkas Date: Thu, 5 Jun 2025 18:18:47 +0000 Subject: [PATCH 042/117] Welcome new friends, Michael, Jessica, Gleb, Zachary, and Jaylyn (#39891) Adding Jessica and Michael to community.md demoting myself to overhead :) --------- Co-authored-by: Kayla Cinnamon Co-authored-by: Mike Griese --- .github/actions/spell-check/allow/names.txt | 13 ++++++++----- COMMUNITY.md | 12 +++++++----- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/.github/actions/spell-check/allow/names.txt b/.github/actions/spell-check/allow/names.txt index f30d204c72..06b8572a26 100644 --- a/.github/actions/spell-check/allow/names.txt +++ b/.github/actions/spell-check/allow/names.txt @@ -46,8 +46,8 @@ betsegaw bricelam bsky CCcat -chenmy chemwolf +chenmy Chinh chrdavis Chrzan @@ -65,8 +65,8 @@ Deondre DHowett ductdo Essey -Feng ethanfangg +Feng ferraridavide foxmsft frankychen @@ -77,6 +77,7 @@ Galaxi Garside Gershaft Giordani +Gleb Gokce gordon Griese @@ -95,6 +96,7 @@ Huynh Ionut jamrobot Jaswal +Jaylyn jefflord Jordi jyuwono @@ -105,6 +107,7 @@ Kantarci Karthick kaylacinnamon kevinguo +Khmyznikov Krigun Lambson Laute @@ -148,11 +151,13 @@ ricardosantos riri ritchielawrence robmikh +ruslanlap Russinovich Rutkas ryanbodrug saahmedm sachaple +Sameerjs Santossio Schoen Sekan @@ -171,6 +176,7 @@ TBM tilovell Triet urnotdfs +vednig waaverecords wang Whuihuan @@ -189,9 +195,6 @@ zhaopy zhaoqpcn Zoltan Zykova -Sameerjs -ruslanlap -vednig # OTHERS diff --git a/COMMUNITY.md b/COMMUNITY.md index f6bfa8d67a..411aad304b 100644 --- a/COMMUNITY.md +++ b/COMMUNITY.md @@ -117,10 +117,6 @@ PowerRename is from Chris's SmartRename and icon rendering for SVGs in File Expl PowerToys Awake is a tool to keep your computer awake. -### [@Niels9001](https://github.com/niels9001/) - [Niels Laute](https://nielslaute.com/) - -Niels has helped drive large sums of our update toward a new [consistent and modern UX](https://github.com/microsoft/PowerToys/issues/891). This includes the [launcher work](https://github.com/microsoft/PowerToys/issues/44), color picker UX update and [icon design](https://github.com/microsoft/PowerToys/issues/1118). - ### [@randyrants](https://github.com/randyrants) - [Randy Santossio](https://www.randyrants.com) Randy contributed Registry Preview and some very early conversations about keyboard remapping. @@ -184,10 +180,10 @@ ZoomIt source code was originally implemented by [Sysinternals](https://sysinter ## PowerToys core team -- [@crutkas](https://github.com/crutkas/) - Clint Rutkas - Lead - [@cinnamon-msft](https://github.com/cinnamon-msft) - Kayla Cinnamon - Lead - [@nguyen-dows](https://github.com/nguyen-dows) - Christopher Nguyen - Product Manager - [@craigloewen-msft](https://github.com/craigloewen-msft) - Craig Loewen - Product Manager +- [@niels9001](https://github.com/niels9001/) - Niels Laute - Product Manager - [@zhiwei-ms](https://github.com/zhiwei-ms) - Zhiwei Yu - Product Manager - [@dhowett](https://github.com/dhowett) - Dustin Howett - Dev lead - [@yeelam-gordon](https://github.com/yeelam-gordon) - Gordon Lam - Dev lead @@ -204,6 +200,12 @@ ZoomIt source code was originally implemented by [Sysinternals](https://sysinter - [@zhaopy536](https://github.com/zhaopy536) - Peiyao Zhao - Dev - [@wang563681252](https://github.com/wang563681252) - Zhaopeng Wang - Dev - [@vanzue](https://github.com/vanzue) - Kai Tao - Dev +- [@zadjii-msft](https://github.com/zadjii-msft) - Mike Griese - Dev +- [@khmyznikov](https://github.com/khmyznikov) - Gleb Khmyznikov - Dev +- [@chatasweetie](https://github.com/chatasweetie) - Jessica Earley-Cha - Dev +- [@MichaelJolley](https://github.com/MichaelJolley) - Michael Jolley - Dev +- [@Jaylyn-Barbee](https://github.com/Jaylyn-Barbee) - Jaylyn Barbee - Dev +- [@crutkas](https://github.com/crutkas/) - Clint Rutkas - Overhead ## Former PowerToys core team members From 309582795cca5f6a8f49acc3f59906daa5ee7a10 Mon Sep 17 00:00:00 2001 From: Davide Giacometti <25966642+davidegiacometti@users.noreply.github.com> Date: Thu, 5 Jun 2025 22:29:18 +0200 Subject: [PATCH 043/117] [CmdPal] Add system tray menu (#39155) ## Summary of the Pull Request image ## PR Checklist - [x] **Closes:** #38303 - [ ] **Communication:** I've discussed this with core contributors already. If work hasn't been agreed, this work might be rejected - [ ] **Tests:** Added/updated and all pass - [ ] **Localization:** All end user facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx ## Detailed Description of the Pull Request / Additional comments - Move all system tray related code from main window code behind to a dedicated service - Add system tray menu ## Validation Steps Performed Manually tested: - Started CmdPal with tray icon enabled - Started CmdPal with tray icon disabled - Enabled/Disabled tray icon - Tested tray menu commands - Verified that the tray icon is visible after restarting explorer.exe --- .github/actions/spell-check/expect.txt | 1 + .../cmdpal/Microsoft.CmdPal.UI/App.xaml.cs | 2 + .../Helpers/TrayIconService.cs | 212 ++++++++++++++++++ .../Microsoft.CmdPal.UI/MainWindow.xaml.cs | 121 +--------- .../Microsoft.CmdPal.UI/NativeMethods.txt | 5 + .../Strings/en-us/Resources.resw | 6 + 6 files changed, 231 insertions(+), 116 deletions(-) create mode 100644 src/modules/cmdpal/Microsoft.CmdPal.UI/Helpers/TrayIconService.cs diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 569cdb93db..ea369d57af 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -789,6 +789,7 @@ LCIDTo Lclean Ldone Ldr +LEFTALIGN LEFTSCROLLBAR LEFTTEXT LError diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/App.xaml.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/App.xaml.cs index 4917dc5f0e..9670c8645f 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/App.xaml.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/App.xaml.cs @@ -20,6 +20,7 @@ using Microsoft.CmdPal.Ext.WindowsSettings; using Microsoft.CmdPal.Ext.WindowsTerminal; using Microsoft.CmdPal.Ext.WindowWalker; using Microsoft.CmdPal.Ext.WinGet; +using Microsoft.CmdPal.UI.Helpers; using Microsoft.CmdPal.UI.ViewModels; using Microsoft.CmdPal.UI.ViewModels.BuiltinCommands; using Microsoft.CmdPal.UI.ViewModels.Models; @@ -140,6 +141,7 @@ public partial class App : Application var state = AppStateModel.LoadState(); services.AddSingleton(state); services.AddSingleton(); + services.AddSingleton(); // ViewModels services.AddSingleton(); diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Helpers/TrayIconService.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/Helpers/TrayIconService.cs new file mode 100644 index 0000000000..3aae253512 --- /dev/null +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Helpers/TrayIconService.cs @@ -0,0 +1,212 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using CommunityToolkit.Mvvm.Messaging; +using Microsoft.CmdPal.UI.ViewModels; +using Microsoft.CmdPal.UI.ViewModels.Messages; +using Microsoft.UI.Xaml; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.UI.Shell; +using Windows.Win32.UI.WindowsAndMessaging; +using WinRT.Interop; +using RS_ = Microsoft.CmdPal.UI.Helpers.ResourceLoaderInstance; + +namespace Microsoft.CmdPal.UI.Helpers; + +[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "Stylistically, window messages are WM_*")] +[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1306:Field names should begin with lower-case letter", Justification = "Stylistically, window messages are WM_*")] +internal sealed partial class TrayIconService +{ + private const uint MY_NOTIFY_ID = 1000; + private const uint WM_TRAY_ICON = PInvoke.WM_USER + 1; + + private readonly SettingsModel _settingsModel; + private readonly uint WM_TASKBAR_RESTART; + + private Window? _window; + private HWND _hwnd; + private WNDPROC? _originalWndProc; + private WNDPROC? _trayWndProc; + private NOTIFYICONDATAW? _trayIconData; + private DestroyIconSafeHandle? _largeIcon; + private DestroyMenuSafeHandle? _popupMenu; + + public TrayIconService(SettingsModel settingsModel) + { + _settingsModel = settingsModel; + + // TaskbarCreated is the message that's broadcast when explorer.exe + // restarts. We need to know when that happens to be able to bring our + // notification area icon back + WM_TASKBAR_RESTART = PInvoke.RegisterWindowMessage("TaskbarCreated"); + } + + public void SetupTrayIcon(bool? showSystemTrayIcon = null) + { + if (showSystemTrayIcon ?? _settingsModel.ShowSystemTrayIcon) + { + if (_window == null) + { + _window = new Window(); + _hwnd = new HWND(WindowNative.GetWindowHandle(_window)); + + // LOAD BEARING: If you don't stick the pointer to HotKeyPrc into a + // member (and instead like, use a local), then the pointer we marshal + // into the WindowLongPtr will be useless after we leave this function, + // and our **WindProc will explode**. + _trayWndProc = WindowProc; + var hotKeyPrcPointer = Marshal.GetFunctionPointerForDelegate(_trayWndProc); + _originalWndProc = Marshal.GetDelegateForFunctionPointer(PInvoke.SetWindowLongPtr(_hwnd, WINDOW_LONG_PTR_INDEX.GWL_WNDPROC, hotKeyPrcPointer)); + } + + if (_trayIconData == null) + { + // We need to stash this handle, so it doesn't clean itself up. If + // explorer restarts, we'll come back through here, and we don't + // really need to re-load the icon in that case. We can just use + // the handle from the first time. + _largeIcon = GetAppIconHandle(); + _trayIconData = new NOTIFYICONDATAW() + { + cbSize = (uint)Marshal.SizeOf(typeof(NOTIFYICONDATAW)), + hWnd = _hwnd, + uID = MY_NOTIFY_ID, + uFlags = NOTIFY_ICON_DATA_FLAGS.NIF_MESSAGE | NOTIFY_ICON_DATA_FLAGS.NIF_ICON | NOTIFY_ICON_DATA_FLAGS.NIF_TIP, + uCallbackMessage = WM_TRAY_ICON, + hIcon = (HICON)_largeIcon.DangerousGetHandle(), + szTip = RS_.GetString("AppStoreName"), + }; + } + + var d = (NOTIFYICONDATAW)_trayIconData; + + // Add the notification icon + PInvoke.Shell_NotifyIcon(NOTIFY_ICON_MESSAGE.NIM_ADD, in d); + + if (_popupMenu == null) + { + _popupMenu = PInvoke.CreatePopupMenu_SafeHandle(); + PInvoke.InsertMenu(_popupMenu, 0, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_STRING, PInvoke.WM_USER + 1, RS_.GetString("TrayMenu_Settings")); + PInvoke.InsertMenu(_popupMenu, 1, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_STRING, PInvoke.WM_USER + 2, RS_.GetString("TrayMenu_Exit")); + } + } + else + { + Destroy(); + } + } + + public void Destroy() + { + if (_trayIconData != null) + { + var d = (NOTIFYICONDATAW)_trayIconData; + if (PInvoke.Shell_NotifyIcon(NOTIFY_ICON_MESSAGE.NIM_DELETE, in d)) + { + _trayIconData = null; + } + } + + if (_popupMenu != null) + { + _popupMenu.Close(); + _popupMenu = null; + } + + if (_largeIcon != null) + { + _largeIcon.Close(); + _largeIcon = null; + } + + if (_window != null) + { + _window.Close(); + _window = null; + _hwnd = HWND.Null; + } + } + + private DestroyIconSafeHandle GetAppIconHandle() + { + var exePath = System.Reflection.Assembly.GetExecutingAssembly().Location; + DestroyIconSafeHandle largeIcon; + PInvoke.ExtractIconEx(exePath, 0, out largeIcon, out _, 1); + return largeIcon; + } + + private LRESULT WindowProc( + HWND hwnd, + uint uMsg, + WPARAM wParam, + LPARAM lParam) + { + switch (uMsg) + { + case PInvoke.WM_COMMAND: + { + if (wParam == PInvoke.WM_USER + 1) + { + WeakReferenceMessenger.Default.Send(); + } + else if (wParam == PInvoke.WM_USER + 2) + { + WeakReferenceMessenger.Default.Send(); + } + } + + break; + + // Shell_NotifyIcon can fail when we invoke it during the time explorer.exe isn't present/ready to handle it. + // We'll also never receive WM_TASKBAR_RESTART message if the first call to Shell_NotifyIcon failed, so we use + // WM_WINDOWPOSCHANGING which is always received on explorer startup sequence. + case PInvoke.WM_WINDOWPOSCHANGING: + { + if (_trayIconData == null) + { + SetupTrayIcon(); + } + } + + break; + default: + // WM_TASKBAR_RESTART isn't a compile-time constant, so we can't + // use it in a case label + if (uMsg == WM_TASKBAR_RESTART) + { + // Handle the case where explorer.exe restarts. + // Even if we created it before, do it again + SetupTrayIcon(); + } + else if (uMsg == WM_TRAY_ICON) + { + switch ((uint)lParam.Value) + { + case PInvoke.WM_RBUTTONUP: + { + if (_popupMenu != null) + { + PInvoke.GetCursorPos(out var cursorPos); + PInvoke.SetForegroundWindow(_hwnd); + PInvoke.TrackPopupMenuEx(_popupMenu, (uint)TRACK_POPUP_MENU_FLAGS.TPM_LEFTALIGN | (uint)TRACK_POPUP_MENU_FLAGS.TPM_BOTTOMALIGN, cursorPos.X, cursorPos.Y, _hwnd, null); + } + } + + break; + case PInvoke.WM_LBUTTONUP: + case PInvoke.WM_LBUTTONDBLCLK: + WeakReferenceMessenger.Default.Send(new(string.Empty, HWND.Null)); + break; + } + } + + break; + } + + return PInvoke.CallWindowProc(_originalWndProc, hwnd, uMsg, wParam, lParam); + } +} diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/MainWindow.xaml.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/MainWindow.xaml.cs index d4d868b581..f66ddf5445 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/MainWindow.xaml.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/MainWindow.xaml.cs @@ -29,7 +29,6 @@ using Windows.Win32; using Windows.Win32.Foundation; using Windows.Win32.Graphics.Dwm; using Windows.Win32.UI.Input.KeyboardAndMouse; -using Windows.Win32.UI.Shell; using Windows.Win32.UI.WindowsAndMessaging; using WinRT; using WinUIEx; @@ -47,22 +46,8 @@ public sealed partial class MainWindow : WindowEx, private readonly WNDPROC? _hotkeyWndProc; private readonly WNDPROC? _originalWndProc; private readonly List _hotkeys = []; - private bool _ignoreHotKeyWhenFullScreen = true; - - // Stylistically, window messages are WM_* -#pragma warning disable SA1310 // Field names should not contain underscore -#pragma warning disable SA1306 // Field names should begin with lower-case letter - private const uint MY_NOTIFY_ID = 1000; - private const uint WM_TRAY_ICON = PInvoke.WM_USER + 1; - private readonly uint WM_TASKBAR_RESTART; -#pragma warning restore SA1306 // Field names should begin with lower-case letter -#pragma warning restore SA1310 // Field names should not contain underscore - private readonly KeyboardListener _keyboardListener; - - // Notification Area ("Tray") icon data - private NOTIFYICONDATAW? _trayIconData; - private DestroyIconSafeHandle? _largeIcon; + private bool _ignoreHotKeyWhenFullScreen = true; private DesktopAcrylicController? _acrylicController; private SystemBackdropConfiguration? _configurationSource; @@ -79,11 +64,6 @@ public sealed partial class MainWindow : WindowEx, _keyboardListener.SetProcessCommand(new CmdPalKeyboardService.ProcessCommand(HandleSummon)); - // TaskbarCreated is the message that's broadcast when explorer.exe - // restarts. We need to know when that happens to be able to bring our - // notification area icon back - WM_TASKBAR_RESTART = PInvoke.RegisterWindowMessage("TaskbarCreated"); - this.SetIcon(); AppWindow.Title = RS_.GetString("AppName"); PositionCentered(); @@ -159,7 +139,7 @@ public sealed partial class MainWindow : WindowEx, var settings = App.Current.Services.GetService()!; SetupHotkey(settings); - SetupTrayIcon(settings.ShowSystemTrayIcon); + App.Current.Services.GetService()!.SetupTrayIcon(settings.ShowSystemTrayIcon); _ignoreHotKeyWhenFullScreen = settings.IgnoreShortcutWhenFullscreen; @@ -219,7 +199,7 @@ public sealed partial class MainWindow : WindowEx, private void ShowHwnd(IntPtr hwndValue, MonitorBehavior target) { - var hwnd = new HWND(hwndValue); + var hwnd = new HWND(hwndValue != 0 ? hwndValue : _hwnd); // Remember, IsIconic == "minimized", which is entirely different state // from "show/hide" @@ -332,7 +312,7 @@ public sealed partial class MainWindow : WindowEx, var extensionService = serviceProvider.GetService()!; extensionService.SignalStopExtensionsAsync(); - RemoveTrayIcon(); + App.Current.Services.GetService()!.Destroy(); // WinUI bug is causing a crash on shutdown when FailFastOnErrors is set to true (#51773592). // Workaround by turning it off before shutdown. @@ -340,6 +320,7 @@ public sealed partial class MainWindow : WindowEx, DisposeAcrylic(); _keyboardListener.Stop(); + Environment.Exit(0); } private void DisposeAcrylic() @@ -606,100 +587,8 @@ public sealed partial class MainWindow : WindowEx, return (LRESULT)IntPtr.Zero; } - - // Shell_NotifyIcon can fail when we invoke it during the time explorer.exe isn't present/ready to handle it. - // We'll also never receive WM_TASKBAR_RESTART message if the first call to Shell_NotifyIcon failed, so we use - // WM_WINDOWPOSCHANGING which is always received on explorer startup sequence. - case PInvoke.WM_WINDOWPOSCHANGING: - { - if (_trayIconData == null) - { - SetupTrayIcon(); - } - } - - break; - default: - // WM_TASKBAR_RESTART isn't a compile-time constant, so we can't - // use it in a case label - if (uMsg == WM_TASKBAR_RESTART) - { - // Handle the case where explorer.exe restarts. - // Even if we created it before, do it again - SetupTrayIcon(); - } - else if (uMsg == WM_TRAY_ICON) - { - switch ((uint)lParam.Value) - { - case PInvoke.WM_RBUTTONUP: - case PInvoke.WM_LBUTTONUP: - case PInvoke.WM_LBUTTONDBLCLK: - Summon(string.Empty); - break; - } - } - - break; } return PInvoke.CallWindowProc(_originalWndProc, hwnd, uMsg, wParam, lParam); } - - private void SetupTrayIcon(bool? showSystemTrayIcon = null) - { - if (showSystemTrayIcon ?? App.Current.Services.GetService()!.ShowSystemTrayIcon) - { - // We only need to build the tray data once. - if (_trayIconData == null) - { - // We need to stash this handle, so it doesn't clean itself up. If - // explorer restarts, we'll come back through here, and we don't - // really need to re-load the icon in that case. We can just use - // the handle from the first time. - _largeIcon = GetAppIconHandle(); - _trayIconData = new NOTIFYICONDATAW() - { - cbSize = (uint)Marshal.SizeOf(typeof(NOTIFYICONDATAW)), - hWnd = _hwnd, - uID = MY_NOTIFY_ID, - uFlags = NOTIFY_ICON_DATA_FLAGS.NIF_MESSAGE | NOTIFY_ICON_DATA_FLAGS.NIF_ICON | NOTIFY_ICON_DATA_FLAGS.NIF_TIP, - uCallbackMessage = WM_TRAY_ICON, - hIcon = (HICON)_largeIcon.DangerousGetHandle(), - szTip = RS_.GetString("AppStoreName"), - }; - } - - var d = (NOTIFYICONDATAW)_trayIconData; - - // Add the notification icon - PInvoke.Shell_NotifyIcon(NOTIFY_ICON_MESSAGE.NIM_ADD, in d); - } - else - { - RemoveTrayIcon(); - } - } - - private void RemoveTrayIcon() - { - if (_trayIconData != null) - { - var d = (NOTIFYICONDATAW)_trayIconData; - if (PInvoke.Shell_NotifyIcon(NOTIFY_ICON_MESSAGE.NIM_DELETE, in d)) - { - _trayIconData = null; - } - } - - _largeIcon?.Close(); - } - - private DestroyIconSafeHandle GetAppIconHandle() - { - var exePath = System.Reflection.Assembly.GetExecutingAssembly().Location; - DestroyIconSafeHandle largeIcon; - PInvoke.ExtractIconEx(exePath, 0, out largeIcon, out _, 1); - return largeIcon; - } } diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/NativeMethods.txt b/src/modules/cmdpal/Microsoft.CmdPal.UI/NativeMethods.txt index c39ed38300..427742f95f 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/NativeMethods.txt +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/NativeMethods.txt @@ -35,9 +35,14 @@ WM_WINDOWPOSCHANGING RegisterWindowMessageW GetModuleHandleW ExtractIconEx +TRACK_POPUP_MENU_FLAGS +WM_COMMAND WM_RBUTTONUP WM_LBUTTONUP WM_LBUTTONDBLCLK +CreatePopupMenu +TrackPopupMenuEx +InsertMenu MessageBox DwmGetWindowAttribute diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Strings/en-us/Resources.resw b/src/modules/cmdpal/Microsoft.CmdPal.UI/Strings/en-us/Resources.resw index 6c63eeff16..efaf667230 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Strings/en-us/Resources.resw +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Strings/en-us/Resources.resw @@ -412,4 +412,10 @@ Right-click to remove the key combination, thereby deactivating the shortcut. More + + Settings + + + Exit + \ No newline at end of file From c1e49ba915712b36800411f380a9f836c172f52e Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 5 Jun 2025 21:50:51 -0500 Subject: [PATCH 044/117] CmdPal: Don't trim the template proj in debug builds (#39463) This saves a LOT of compilation time --- .../TemplateCmdPalExtension.csproj | 28 ++++++++++++++++-- .../Assets/template.zip | Bin 18702 -> 18987 bytes 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/TemplateCmdPalExtension.csproj b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/TemplateCmdPalExtension.csproj index af4ca3525d..644599cc90 100644 --- a/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/TemplateCmdPalExtension.csproj +++ b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/TemplateCmdPalExtension.csproj @@ -56,13 +56,35 @@ + true true + true 2 - IL2081 + IL2081;$(WarningsNotAsErrors) + + + true - true - true + + + + false + + true + true + true + + + + + true + + + diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Assets/template.zip b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Assets/template.zip index d4ff0e60685006d7472fdc13073658ae0cf23d8d..f026bbba8b238d8ec1ca59b5c009830328697522 100644 GIT binary patch delta 3179 zcmZ`*2Uru?8chNOLX{?vsDM;KNrDhUQ<{Jfs&tepMS2kx1w#{gkEIU9EWN2HO^Q)L zDN+R#DS|!_LP{x0hL@duGfB>O&iViU-nsw1shRNWS#TapQx;ZE8114a z)MoG~@n3V1_1y$HAdsZb0)iQtohXpv7(#LAry+~uINiQ8`v8R0SESZ(t&cN<1_%^!e&73MOZh8fWURq z>;1bHle2@CeKwPAz7L7{K8U7&@O+O}`!1-`c%T#YdxU`i}_cNNap8v3@;OV0N@+weZ7*^45EvwSuSm61m3pn&$P%@ z?m{94E*4KkDY(hhpK4tp4^#C*lkF|aO?pT+sl#hSi`he}L51F#J`@||Y+m1k%EC9o zJ+>}k?hc$~qM!D=-y*hgsft{=o3;vETW-8~;_0%}#V-Ut^^x!_!ww>Gx&5&p^`%7r z{X-Adt3`r#MeXtJb+3Brp&;YqLBJ^(!2_RZ&uGj~D)F>ME<5|jlLdX>SLhW)9G9pz zUOmd0l`kp9zoeDe&~53D`&x^-X8lMm1F6fXNaOEu+&3Lz((*QOgp&o4yD zaQtnjUa_L?q8c~9lb4+8_qB6LF@Jr~TlW+kC3scOV@?Ncm%}rQnNuJ1?IrFvRc2>P z2uq!6Q#aoh>+>keXQzcBIkU8BMo%|jtQu8?mcVBw^zw_X`g)RZv&LO2ju~xI9328Z zA2Hk8oiqBn#9wLjoD^o8<-j?u5?R6@M~;eQbh%z+@oc%tQ1= zb|Vo|?Sqh)H}4Y@|IDqRii+WeMUg7>;~gX$sK*jgl} z6>&PwJO?c$OtNw%w|{i{@089TD(nv>LdNrA&a$qoqSsRVTD!J+x-5UgC$%W#C5|<{ zAJ6NOiVV8oknXCaoLX;N{=%_fKr1-j;3j1+_u)|XxMPR*Mau{u@i@0rc)>aOZAyfV z*nyR%M(3mVs`GD#u2b$rtY!B(uFw22QF=GJexzl>ZDbKHrJY4azQ^8wh^d)!bsEQV zx;oxE3%qDxnNL%w5kL1zkryNM#m3Rr#l2&#*ty%zPK$Spv`~f`x!%`hJtWk_Hs9|q zRKhlkFPD^GuN#YP4mxT2LF!S=?3))!*$$j%HE>DOK6twsWt2 zom)jRqU*M^kBkqKxmPNz1&W-@t_Q8pKkSJw`@PFEs`-h|lcmPoJ4I!_4ZC$~jD<~) zBv}}nh{wFyk<6#qEnj@up~2juFuVBc{rsg%F=a{x5ho+MI`5Cp+W9z$w_RBi=BusW z+4!aO_fO6p-nYZbCbK)(64n-OZ(k@diX-}mo{Ih)g%4m2wMrx7QMuBI33HSA#br0N zdsSKAlg>-96|IXsCz(oGy^9s7ViNY6;SMucav*0fW&tV0yck)Upy|f^Y1)$y(-2RSQ}e#AgIhVvGj6 zyC^X{Am~SP{}}+2-OMQqJd`Sms0}_X27613GnNZm}V2A zi$SEKH%FsecQ;ekI>^cur?W%>6Fo5;F(ndj%6}^^b#fo zT%KouC3CUBU^W{T;9u|pHZ6cz={-!74ARFSiolZ<^o9V7vV2=u^fSyf$l_AiWEC55 z{rkIQWq?5h{z8@5QZ+qgiIVo7V_=Sf;3{!uNO}4fEe6m&B+PWyqA_y%4zyVY^^Oes zdqC_Vccz-LtmCf|!9jTo-sTLN9Dv`@ZcE|xb$iwjgUG;c7CwU&XIjnAlh{`0X8cbr zdI!Vt?B@!mhl(-6Ae_`T=$S|nV9rRCX-@pc7(|%*rmveZKhvCET$(s|3#`-v93^Rn zYNs?ot)d?NWn=@FE4X054-XFvHlqcD$;0pjHDv<9UDaFN+e5`0@2-YZ_f}Q$#1Yk0 SJe6_k9_~1A4|P>luYUlZyL^=Z delta 2868 zcmai02~-nV7OfBhVc#+k5=CVRt07@aB0B*Li^w7fD6%IY$mT+$NJMRBx{+f*@)mEz*@L=sDr!oJr14a&Fyw-}~>S-miZaptAE( za-MEzacMyCnvSZ=lOrp6R1u>#K`acqGtm&3m)|1=Q36$_>QJ6wFv?BG(hxn83FqKnVenV2QU(*~hJD6-digG5*y zVi<+E4H(74fc5Th1h__y5CEXGaUSemZX5y=P?R2&lAtqz10`#+;s78e3jiLVm$oTr zXM+d1EE%q#r~mRh7OzQD{F?T3O>)vexlgk2N>cRxAiwI1F~4EXH@|(VaM_^6@*J`0 zC(ap_AA={Cu2*NH*_k`)(DkXBQidLiEceTZ1 zr3UrUk;5GZ#hh=YvXXE2C7A|s6m2L}fBV4^DuY{hM*mS!ZKWxJN&nW2H)LnKk5r)G zCvlPOEuM9j_IG}yI)`fhmRf!Ok$p)nySSp=*8W;Qk581m&(wJzz}rnSORJGtk?mhl zZM~ZIEWWL2Z1?(Fjy-Q)3t0Agy;T0gX<1B>LR!n$!(FEyT(##7Cg+5mKUm>* zIlG8EV(B`^Y-zc0J>j^SS9zqjI%}ai%g6i|ne39gQ(ZT38B(u)FMS>M54DD->z1}- z@eNPsfRf|}{A?B{Z-YN%~xk0;jUGC_AaC!`8hUQF)SWr`)Iev|Ev0-RH z&kyK&L7n!Dr5%HZAMbTOzFGX zkNMnri5d4w>F)x>mKu$$eXQ8trQz+a6OHpB5iaIR zPjk)j+RpV|hT#s%ldI3?UZ>4t**c*XRzcjRQ;8|vA8KWD;>O&RS!n^5fB5o)+SqPw z&h;#8SdPI;!|+M+1j*_CuK&(7TuSt1Hze^1pR6BJF`60M%lX=K131ZyGFpi2%2?I4 zWXBZwoXqyg7b|mVt7oI!)!Fm6{EJ4Pa3r4aTPlAE46pg3KRd7<|43TypOEd zlD__2T=LEl-=UYj-V7a&7hZj7%|%B7_MYjkYcG79Iq>`CXZmq8pZ(+x3d3+mDsvo^ z7|`!_KB^nMEHl0blq{?#6eQY;+u;EkYH$5sxgJUIddnF%M^lJ4Mak|8dwN7L# zF>mUe>!P@6zBx4N-$y?-YO{a~4X&8}`Gfz}at%G5CBE6+V>Is3*O@J0{KX)1U!#3G zZ9ng!EZiwvp3xesof9YtTux=?n#dUPU{= z8%#4WnyL1G+i&q#N`m&&z$T`RsD$zziKqhTA_IJ*ttE;8W;znZDoIdWQ*ePKzlDPQ zg}y)(Sp%42=PZr)J%9mF!W%<)aZW_BxJ@2G6c1_rBE);O$ukyNqq9U^roW(XP}c)C zwu;oK!)PNB4RFy}L8SP3LBv!BRF&VLPPh+4r(W>V?}-C|#&Su0rB` zV?kf3@N>o2y^buncsiCKmErmu3Q^q2qtt}891*Zmf(C{L8WEUdZmyYl{ z8A>8KRS>6hkAR~Nj<_f}APvJn_bPNpKyNuY0O+=a?r?x^9&TY`!H6`YnV3_}EyAem Z$OsFDIh|^1!KRzhO(>=&5#bC5`%fKN(C7dF From a1cf836c6ddcb19ad6db62afc0722817fe4bd120 Mon Sep 17 00:00:00 2001 From: Kai Tao <69313318+vanzue@users.noreply.github.com> Date: Fri, 6 Jun 2025 13:46:47 +0800 Subject: [PATCH 045/117] [Workspaces] Change workspaces icon store position (#39677) ## Summary of the Pull Request Before, workspaces ico are stored in temp folder, which is easily cleaned up by user. After clean, user has to explicitly save icon once again. With this change, icons are stored at the same position with workspaces settings ## PR Checklist - [X] **Closes:** #35004 - [ ] **Communication:** I've discussed this with core contributors already. If work hasn't been agreed, this work might be rejected - [ ] **Tests:** Added/updated and all pass - [ ] **Localization:** All end user facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx ## Detailed Description of the Pull Request / Additional comments ## Validation Steps Performed ![image](https://github.com/user-attachments/assets/66723ba6-bb4e-4768-ba06-df105ea7f65e) --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> --- .../ViewModels/MainViewModel.cs | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/modules/Workspaces/WorkspacesEditor/ViewModels/MainViewModel.cs b/src/modules/Workspaces/WorkspacesEditor/ViewModels/MainViewModel.cs index b0974e1241..adccf64f33 100644 --- a/src/modules/Workspaces/WorkspacesEditor/ViewModels/MainViewModel.cs +++ b/src/modules/Workspaces/WorkspacesEditor/ViewModels/MainViewModel.cs @@ -193,27 +193,29 @@ namespace WorkspacesEditor.ViewModels ApplyShortcut(editedProject); } + private string GetDesktopShortcutAddress(Project project) => Path.Combine(FolderUtils.Desktop(), project.Name + ".lnk"); + + private string GetShortcutStoreAddress(Project project) + { + var dataFolder = FolderUtils.DataFolder(); + Directory.CreateDirectory(dataFolder); + var shortcutStoreFolder = Path.Combine(dataFolder, "WorkspacesIcons"); + Directory.CreateDirectory(shortcutStoreFolder); + return Path.Combine(shortcutStoreFolder, project.Id + ".ico"); + } + private void ApplyShortcut(Project project) { - string basePath = AppDomain.CurrentDomain.BaseDirectory; - string shortcutAddress = Path.Combine(FolderUtils.Desktop(), project.Name + ".lnk"); - string shortcutIconFilename = Path.Combine(FolderUtils.Temp(), project.Id + ".ico"); - if (!project.IsShortcutNeeded) { - if (File.Exists(shortcutIconFilename)) - { - File.Delete(shortcutIconFilename); - } - - if (File.Exists(shortcutAddress)) - { - File.Delete(shortcutAddress); - } - + RemoveShortcut(project); return; } + var basePath = AppDomain.CurrentDomain.BaseDirectory; + var shortcutAddress = GetDesktopShortcutAddress(project); + var shortcutIconFilename = GetShortcutStoreAddress(project); + Bitmap icon = WorkspacesIcon.DrawIcon(WorkspacesIcon.IconTextFromProjectName(project.Name), App.ThemeManager.GetCurrentTheme()); WorkspacesIcon.SaveIcon(icon, shortcutIconFilename); @@ -360,8 +362,8 @@ namespace WorkspacesEditor.ViewModels private void RemoveShortcut(Project selectedProject) { - string shortcutAddress = Path.Combine(FolderUtils.Desktop(), selectedProject.Name + ".lnk"); - string shortcutIconFilename = Path.Combine(FolderUtils.Temp(), selectedProject.Id + ".ico"); + string shortcutAddress = GetDesktopShortcutAddress(selectedProject); + string shortcutIconFilename = GetShortcutStoreAddress(selectedProject); if (File.Exists(shortcutIconFilename)) { From 96c5cf18972e2a02167d5ef90b9ddfa79323149f Mon Sep 17 00:00:00 2001 From: leileizhang Date: Fri, 6 Jun 2025 14:08:04 +0800 Subject: [PATCH 046/117] Add missing codec detection and warning message in video preview (#39726) ## Summary of the Pull Request This PR adds support for detecting missing video codecs during video file preview, and displays a warning message to inform the user when the required codec is not installed. ### Changes - Added `GetMissingCodecAsync` to detect unsupported video formats using `MediaEncodingProfile` and `CodecQuery`. - If a required codec is missing, display a warning in the preview UI. - Added localized string `MissingVideoCodec_WarningMessage`: > "This video uses the {0} format, which isn't supported. Please install the required codec to play it." - Add Store Search ![image](https://github.com/user-attachments/assets/7325c3fb-b6ea-4d22-b226-f56e617ac5e2) ![search](https://github.com/user-attachments/assets/e30c1aca-beaf-4d31-a704-abd89279f5c1) ## PR Checklist - [x] **Closes:** https://github.com/microsoft/PowerToys/issues/39235, https://github.com/microsoft/PowerToys/issues/38201 - [ ] **Communication:** I've discussed this with core contributors already. If work hasn't been agreed, this work might be rejected - [ ] **Tests:** Added/updated and all pass - [x] **Localization:** All end user facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx ## Detailed Description of the Pull Request / Additional comments ## Validation Steps Performed To simulate a missing codec (e.g., AV1): 1. Check if the AV1 extension is installed: ```powershell Get-AppxPackage -Name *AV1* 2. If installed, remove it: ```powershell Get-AppxPackage -Name *AV1* | Remove-AppxPackage 3. Use Peek to preview a video encoded in AV1 format. 4. Confirm that a warning message is shown indicating the missing codec. --- .../peek/Peek.FilePreviewer/FilePreview.xaml | 15 +++++++ .../Peek.FilePreviewer/FilePreview.xaml.cs | 23 ++++++++++ .../Previewers/Interfaces/IVideoPreviewer.cs | 2 + .../MediaPreviewer/VideoPreviewer.cs | 43 +++++++++++++++++++ .../peek/Peek.UI/Strings/en-us/Resources.resw | 6 +++ 5 files changed, 89 insertions(+) diff --git a/src/modules/peek/Peek.FilePreviewer/FilePreview.xaml b/src/modules/peek/Peek.FilePreviewer/FilePreview.xaml index c3bbe50c61..311fbd0227 100644 --- a/src/modules/peek/Peek.FilePreviewer/FilePreview.xaml +++ b/src/modules/peek/Peek.FilePreviewer/FilePreview.xaml @@ -60,6 +60,21 @@ + + + + + + + + + + GetMissingCodecAsync(StorageFile? file) + { + try + { + var profile = await MediaEncodingProfile.CreateFromFileAsync(file); + + if (profile.Video != null) + { + var codecQuery = new CodecQuery(); + var decoders = await codecQuery.FindAllAsync( + CodecKind.Video, + CodecCategory.Decoder, + profile.Video.Subtype); + + return decoders.Count > 0 ? string.Empty : profile.Video.Subtype; + } + else + { + Logger.LogWarning($"No video profile found for file {file?.Path}. Cannot determine codec support."); + } + } + catch (Exception ex) + { + Logger.LogError($"Error checking codec support for file {file?.Path}: {ex.Message}"); + } + + return string.Empty; + } + private Task LoadVideoAsync(CancellationToken cancellationToken) { return TaskExtension.RunSafe(async () => @@ -98,10 +134,17 @@ namespace Peek.FilePreviewer.Previewers var storageFile = await Item.GetStorageItemAsync() as StorageFile; + var missingCodecName = await GetMissingCodecAsync(storageFile); + await Dispatcher.RunOnUiThread(() => { cancellationToken.ThrowIfCancellationRequested(); + if (!string.IsNullOrEmpty(missingCodecName)) + { + MissingCodecName = missingCodecName; + } + Preview = MediaSource.CreateFromStorageFile(storageFile); }); }); diff --git a/src/modules/peek/Peek.UI/Strings/en-us/Resources.resw b/src/modules/peek/Peek.UI/Strings/en-us/Resources.resw index 4ffec5f685..f45f961268 100644 --- a/src/modules/peek/Peek.UI/Strings/en-us/Resources.resw +++ b/src/modules/peek/Peek.UI/Strings/en-us/Resources.resw @@ -372,4 +372,10 @@ Don't show this warning again + + Your device doesn't support the {0} format. To play this file, install a codec that supports this format. + + + Search in Microsoft Store + \ No newline at end of file From 4f3f7d649f1548362e12471b81c753ef0289ebe4 Mon Sep 17 00:00:00 2001 From: Michael Jolley Date: Fri, 6 Jun 2025 12:47:54 -0500 Subject: [PATCH 047/117] CmdPal: Improving order of results from settings search (#39525) The search results when finding settings with `$` was sorted alphabetically. Updated this behavior to sort them based on relevance to the query. Original search results with query provided in issue: ![image](https://github.com/user-attachments/assets/7ffd41bc-42d4-46fe-925e-aed7d7d04f3f) New search results with query provided in original issue: ![Image](https://github.com/user-attachments/assets/ab47fc25-9b90-4cc7-b476-253b23cee6f9) Example of results when giving a query that matches many, but is also an exact match. ![Image](https://github.com/user-attachments/assets/59982f41-d27c-4582-9b01-9a5683a26819) Closes #38615 --- .../Pages/WindowsSettingsListPage.cs | 52 ++++++++++++++----- 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Pages/WindowsSettingsListPage.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Pages/WindowsSettingsListPage.cs index 994db10445..3f5a6c6217 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Pages/WindowsSettingsListPage.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Pages/WindowsSettingsListPage.cs @@ -31,57 +31,81 @@ internal sealed partial class WindowsSettingsListPage : DynamicListPage } var filteredList = _windowsSettings.Settings - .Where(Predicate) - .OrderBy(found => found.Name); + .Select(SearchScoringPredicate) + .Where(scoredSetting => scoredSetting.Score > 0) + .OrderByDescending(scoredSetting => scoredSetting.Score) + .Select(scoredSetting => scoredSetting.Setting); var newList = ResultHelper.GetResultList(filteredList, query); return newList; - bool Predicate(WindowsSetting found) + // Rank settings by how they matched the search query. Order is: + // 1. Exact Name (10 points) + // 2. Name Starts With (8 points) + // 3. Name (5 points) + // 4. Area (4 points) + // 5. AltName (2 points) + // 6. Settings path (1 point) + (WindowsSetting Setting, int Score) SearchScoringPredicate(WindowsSetting setting) { if (string.IsNullOrWhiteSpace(query)) { // If no search string is entered skip query comparison. - return true; + return (setting, 0); } - if (found.Name.Contains(query, StringComparison.CurrentCultureIgnoreCase)) + if (string.Equals(setting.Name, query, StringComparison.OrdinalIgnoreCase)) { - return true; + return (setting, 10); } - if (!(found.Areas is null)) + if (setting.Name.StartsWith(query, StringComparison.CurrentCultureIgnoreCase)) { - foreach (var area in found.Areas) + return (setting, 8); + } + + if (setting.Name.Contains(query, StringComparison.CurrentCultureIgnoreCase)) + { + return (setting, 5); + } + + if (!(setting.Areas is null)) + { + foreach (var area in setting.Areas) { // Search for areas on normal queries. if (area.Contains(query, StringComparison.CurrentCultureIgnoreCase)) { - return true; + return (setting, 4); } // Search for Area only on queries with action char. if (area.Contains(query.Replace(":", string.Empty), StringComparison.CurrentCultureIgnoreCase) && query.EndsWith(":", StringComparison.CurrentCultureIgnoreCase)) { - return true; + return (setting, 4); } } } - if (!(found.AltNames is null)) + if (!(setting.AltNames is null)) { - foreach (var altName in found.AltNames) + foreach (var altName in setting.AltNames) { if (altName.Contains(query, StringComparison.CurrentCultureIgnoreCase)) { - return true; + return (setting, 2); } } } // Search by key char '>' for app name and settings path - return query.Contains('>') ? ResultHelper.FilterBySettingsPath(found, query) : false; + if (query.Contains('>') && ResultHelper.FilterBySettingsPath(setting, query)) + { + return (setting, 1); + } + + return (setting, 0); } } From 7417b857e8152a0072b63b546635caf1409f22ba Mon Sep 17 00:00:00 2001 From: Muhammad Danish Date: Sat, 7 Jun 2025 08:58:10 +0500 Subject: [PATCH 048/117] Use Microsoft.Windows.Settings module for enabling developer mode (#39334) ## Summary of the Pull Request Using the [Microsoft.Windows.Settings](https://www.powershellgallery.com/packages/Microsoft.Windows.Settings/) module is the now the recommended way of configuring Windows Settings, including developer mode. ## PR Checklist - [ ] **Closes:** #xxx - [ ] **Communication:** I've discussed this with core contributors already. If work hasn't been agreed, this work might be rejected - [ ] **Tests:** Added/updated and all pass - [ ] **Localization:** All end user facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx ## Detailed Description of the Pull Request / Additional comments ## Validation Steps Performed Verified that setting developer mode with the new resource works as intended --- .config/configuration.vsEnterprise.winget | 4 ++-- .config/configuration.vsProfessional.winget | 4 ++-- .config/configuration.winget | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.config/configuration.vsEnterprise.winget b/.config/configuration.vsEnterprise.winget index 4d19d37137..08c9983562 100644 --- a/.config/configuration.vsEnterprise.winget +++ b/.config/configuration.vsEnterprise.winget @@ -2,14 +2,14 @@ # Reference: https://github.com/microsoft/PowerToys/blob/main/doc/devdocs/readme.md#compiling-powertoys properties: resources: - - resource: Microsoft.Windows.Developer/DeveloperMode + - resource: Microsoft.Windows.Settings/WindowsSettings directives: description: Enable Developer Mode allowPrerelease: true # Requires elevation for the set operation securityContext: elevated settings: - Ensure: Present + DeveloperMode: true - resource: Microsoft.WinGet.DSC/WinGetPackage id: vsPackage directives: diff --git a/.config/configuration.vsProfessional.winget b/.config/configuration.vsProfessional.winget index 78fb1c13d5..4d7e4a31c3 100644 --- a/.config/configuration.vsProfessional.winget +++ b/.config/configuration.vsProfessional.winget @@ -2,14 +2,14 @@ # Reference: https://github.com/microsoft/PowerToys/blob/main/doc/devdocs/readme.md#compiling-powertoys properties: resources: - - resource: Microsoft.Windows.Developer/DeveloperMode + - resource: Microsoft.Windows.Settings/WindowsSettings directives: description: Enable Developer Mode allowPrerelease: true # Requires elevation for the set operation securityContext: elevated settings: - Ensure: Present + DeveloperMode: true - resource: Microsoft.WinGet.DSC/WinGetPackage id: vsPackage directives: diff --git a/.config/configuration.winget b/.config/configuration.winget index 456eca47f2..2016dcdc33 100644 --- a/.config/configuration.winget +++ b/.config/configuration.winget @@ -2,14 +2,14 @@ # Reference: https://github.com/microsoft/PowerToys/blob/main/doc/devdocs/readme.md#compiling-powertoys properties: resources: - - resource: Microsoft.Windows.Developer/DeveloperMode + - resource: Microsoft.Windows.Settings/WindowsSettings directives: description: Enable Developer Mode allowPrerelease: true # Requires elevation for the set operation securityContext: elevated settings: - Ensure: Present + DeveloperMode: true - resource: Microsoft.WinGet.DSC/WinGetPackage id: vsPackage directives: From 84296b0d89a3a9548f3e2788c326f0b67622a0fe Mon Sep 17 00:00:00 2001 From: Nathan Gill Date: Sun, 8 Jun 2025 18:06:01 +0100 Subject: [PATCH 049/117] [CmdPal] Remove redundant flag to prevent resource leak (#39865) ## Summary of the Pull Request Remove a redundant flag in the thumbnail helper that leads to a resource leak. ## PR Checklist - [x] **Closes:** #39824 - [ ] **Communication:** I've discussed this with core contributors already. If work hasn't been agreed, this work might be rejected - [ ] **Tests:** Added/updated and all pass - [ ] **Localization:** All end user facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx ## Detailed Description of the Pull Request / Additional comments Remove the `SHGFI_ICON` flag from the call to `NativeMethods.SHGetFileInfo`. This flag opens a handle to the icon (`hIcon`) as well as filling the index (`iIcon`) ([docs](https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shgetfileinfow#shgfi_icon-0x000000100)). This handle stored in `shinfo.hIcon` is never used by following code, and is not freed as suggested by the documentation ([docs](https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shgetfileinfow#remarks)). The `SHGFI_ICON` flag also fills `shinfo.iIcon`, which is used later. However, this is also filled when passing the flag `SHGFI_SYSICONINDEX`, which we already pass. Therefore, the `SHGFI_ICON` flag is redundant here, and has been removed. Thanks to @Androvald for finding and suggesting the fix. --- .../ThumbnailHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/ThumbnailHelper.cs b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/ThumbnailHelper.cs index cf7c5c2073..e06dbfcb22 100644 --- a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/ThumbnailHelper.cs +++ b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/ThumbnailHelper.cs @@ -123,7 +123,7 @@ public class ThumbnailHelper private static nint GetLargestIcon(string path) { var shinfo = default(NativeMethods.SHFILEINFO); - NativeMethods.SHGetFileInfo(path, 0, ref shinfo, (uint)Marshal.SizeOf(shinfo), SHGFI_ICON | SHGFI_SYSICONINDEX); + NativeMethods.SHGetFileInfo(path, 0, ref shinfo, (uint)Marshal.SizeOf(shinfo), SHGFI_SYSICONINDEX); var hIcon = IntPtr.Zero; var iID_IImageList = new Guid("46EB5926-582E-4017-9FDF-E8998DAA0950"); From 6d303af7267fade734795e89782b45cae1a24ff0 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Sun, 8 Jun 2025 12:06:33 -0500 Subject: [PATCH 050/117] CmdPal: Apply the LLKH to all command hotkeys (#39524) We should apply the LLKH setting to all command hotkeys, not just the global one. --- .../Microsoft.CmdPal.UI/MainWindow.xaml.cs | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/MainWindow.xaml.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/MainWindow.xaml.cs index f66ddf5445..13aae5a81b 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/MainWindow.xaml.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/MainWindow.xaml.cs @@ -496,18 +496,27 @@ public sealed partial class MainWindow : WindowEx, if (key != null) { - var vk = key.Code; - var modifiers = - (key.Alt ? HOT_KEY_MODIFIERS.MOD_ALT : 0) | - (key.Ctrl ? HOT_KEY_MODIFIERS.MOD_CONTROL : 0) | - (key.Shift ? HOT_KEY_MODIFIERS.MOD_SHIFT : 0) | - (key.Win ? HOT_KEY_MODIFIERS.MOD_WIN : 0) - ; - - var success = PInvoke.RegisterHotKey(_hwnd, _hotkeys.Count, modifiers, (uint)vk); - if (success) + if (settings.UseLowLevelGlobalHotkey) { - _hotkeys.Add(commandHotkey); + _keyboardListener.SetHotkeyAction(key.Win, key.Ctrl, key.Shift, key.Alt, (byte)key.Code, commandHotkey.CommandId); + + _hotkeys.Add(new(globalHotkey, string.Empty)); + } + else + { + var vk = key.Code; + var modifiers = + (key.Alt ? HOT_KEY_MODIFIERS.MOD_ALT : 0) | + (key.Ctrl ? HOT_KEY_MODIFIERS.MOD_CONTROL : 0) | + (key.Shift ? HOT_KEY_MODIFIERS.MOD_SHIFT : 0) | + (key.Win ? HOT_KEY_MODIFIERS.MOD_WIN : 0) + ; + + var success = PInvoke.RegisterHotKey(_hwnd, _hotkeys.Count, modifiers, (uint)vk); + if (success) + { + _hotkeys.Add(commandHotkey); + } } } } From 310186c44c753ecc998419becee444212f7f5518 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Sun, 8 Jun 2025 19:29:25 -0500 Subject: [PATCH 051/117] CmdPal: Auto-focus first content (#39414) This lets us auto-focus the first input on a `IFormContent`, if there's only one `IContent` on the page. This dramatically improves the usability for forms, since we'll immediately put focus into them Closes #38436 --- .../ContentPageViewModel.cs | 3 + .../ContentViewModel.cs | 1 + .../Controls/ContentFormControl.xaml.cs | 56 +++++++++++++++++++ 3 files changed, 60 insertions(+) diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ContentPageViewModel.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ContentPageViewModel.cs index f8b0e90834..e7a3145e0a 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ContentPageViewModel.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ContentPageViewModel.cs @@ -79,6 +79,9 @@ public partial class ContentPageViewModel : PageViewModel, ICommandBarContext throw; } + var oneContent = newContent.Count == 1; + newContent.ForEach(c => c.OnlyControlOnPage = oneContent); + // Now, back to a UI thread to update the observable collection DoOnUiThread( () => diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ContentViewModel.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ContentViewModel.cs index 9ebf5495fa..21d2d6748f 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ContentViewModel.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ContentViewModel.cs @@ -7,4 +7,5 @@ namespace Microsoft.CmdPal.UI.ViewModels; public abstract partial class ContentViewModel(WeakReference context) : ExtensionObjectViewModel(context) { + public bool OnlyControlOnPage { get; internal set; } } diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/ContentFormControl.xaml.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/ContentFormControl.xaml.cs index 68209d750a..b2afe78dfb 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/ContentFormControl.xaml.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/ContentFormControl.xaml.cs @@ -5,7 +5,9 @@ using AdaptiveCards.ObjectModel.WinUI3; using AdaptiveCards.Rendering.WinUI3; using Microsoft.CmdPal.UI.ViewModels; +using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Media; namespace Microsoft.CmdPal.UI.Controls; @@ -96,11 +98,65 @@ public sealed partial class ContentFormControl : UserControl if (_renderedCard.FrameworkElement != null) { ContentGrid.Children.Add(_renderedCard.FrameworkElement); + + // Use the Loaded event to ensure we focus after the card is in the visual tree + _renderedCard.FrameworkElement.Loaded += OnFrameworkElementLoaded; } _renderedCard.Action += Rendered_Action; } + private void OnFrameworkElementLoaded(object sender, RoutedEventArgs e) + { + // Unhook the event handler to avoid multiple registrations + if (sender is FrameworkElement element) + { + element.Loaded -= OnFrameworkElementLoaded; + + if (!ViewModel?.OnlyControlOnPage ?? true) + { + return; + } + + // Focus on the first focusable element asynchronously to ensure the visual tree is fully built + element.DispatcherQueue.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.Normal, () => + { + var focusableElement = FindFirstFocusableElement(element); + focusableElement?.Focus(FocusState.Programmatic); + }); + } + } + + private Control? FindFirstFocusableElement(DependencyObject parent) + { + var childCount = VisualTreeHelper.GetChildrenCount(parent); + + // Process children first (depth-first search) + for (var i = 0; i < childCount; i++) + { + var child = VisualTreeHelper.GetChild(parent, i); + + // If the child is a focusable control like TextBox, ComboBox, etc. + if (child is Control control && + control.IsEnabled && + control.IsTabStop && + control.Visibility == Visibility.Visible && + control.AllowFocusOnInteraction) + { + return control; + } + + // Recursively check children + var result = FindFirstFocusableElement(child); + if (result != null) + { + return result; + } + } + + return null; + } + private void Rendered_Action(RenderedAdaptiveCard sender, AdaptiveActionEventArgs args) => ViewModel?.HandleSubmit(args.Action, args.Inputs.AsJson()); } From edffb99073d23a8f37c33e72840de3848f751929 Mon Sep 17 00:00:00 2001 From: Gordon Lam <73506701+yeelam-gordon@users.noreply.github.com> Date: Mon, 9 Jun 2025 22:00:12 -0700 Subject: [PATCH 052/117] Log WPF crash exceptions instead of showing Report problem UI (#39918) ## Summary of the Pull Request Problem ------------- We've observed multiple bug reports related to crashes with the following exceptions: System.Runtime.InteropServices.COMException (0xD0000701) System.Runtime.InteropServices.COMException (0x80263001) The root cause of these crashes has been traced to the WPF framework, specifically this line in WindowChromeWorker.cs: https://github.com/dotnet/wpf/blob/3439f20fb8c685af6d9247e8fd2978cac42e74ac/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Shell/WindowChromeWorker.cs#L1005 These crashes are not necessarily caused by PowerToys Run itself being used, or even visible. However, users perceive them as Run-related because the "Report problem UI" is triggered by our global exception handler, surfacing the underlying WPF crash. Fix ------------ This PR: - Suppresses the launch of the "Report problem UI" for exceptions known to be triggered by unstable platform conditions (such as COMExceptions from WPF internals). - Continues to log the exception to preserve diagnostic data. This change ensures we: - Avoid showing an UI when WPF framework itself is already having problem to handling DWM composition changes. --- .../PowerLauncher/Helper/ErrorReporting.cs | 88 ++++++++++++++----- 1 file changed, 64 insertions(+), 24 deletions(-) diff --git a/src/modules/launcher/PowerLauncher/Helper/ErrorReporting.cs b/src/modules/launcher/PowerLauncher/Helper/ErrorReporting.cs index 3eb6252a24..5003a02cae 100644 --- a/src/modules/launcher/PowerLauncher/Helper/ErrorReporting.cs +++ b/src/modules/launcher/PowerLauncher/Helper/ErrorReporting.cs @@ -14,25 +14,7 @@ namespace PowerLauncher.Helper { public static class ErrorReporting { - private static void Report(Exception e, bool waitForClose) - { - if (e != null) - { - var logger = LogManager.GetLogger("UnHandledException"); - logger.Fatal(ExceptionFormatter.FormatException(e)); - - var reportWindow = new ReportWindow(e); - - if (waitForClose) - { - reportWindow.ShowDialog(); - } - else - { - reportWindow.Show(); - } - } - } + private const string LoggerName = "UnHandledException"; public static void ShowMessageBox(string title, string message) { @@ -47,17 +29,20 @@ namespace PowerLauncher.Helper // handle non-ui thread exceptions System.Windows.Application.Current.Dispatcher.Invoke(() => { - Report((Exception)e?.ExceptionObject, true); + HandleException(e?.ExceptionObject as Exception, true); }); } public static void DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e) { - // handle ui thread exceptions - Report(e?.Exception, false); + if (e != null) + { + // handle ui thread exceptions + HandleException(e.Exception, false); - // prevent application exist, so the user can copy prompted error info - e.Handled = true; + // prevent application exist, so the user can copy prompted error info + e.Handled = true; + } } public static string RuntimeInfo() @@ -68,5 +53,60 @@ namespace PowerLauncher.Helper $"\nx64: {Environment.Is64BitOperatingSystem}"; return info; } + + private static void HandleException(Exception e, bool isNotUIThread) + { + // The crash occurs in PresentationFramework.dll, not necessarily when the Runner UI is visible, originating from this line: + // https://github.com/dotnet/wpf/blob/3439f20fb8c685af6d9247e8fd2978cac42e74ac/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Shell/WindowChromeWorker.cs#L1005 + // Many bug reports because users see the "Report problem UI" after "the" crash with System.Runtime.InteropServices.COMException 0xD0000701 or 0x80263001. + // However, displaying this "Report problem UI" during WPF crashes, especially when DWM composition is changing, is not ideal; some users reported it hangs for up to a minute before the "Report problem UI" appears. + // This change modifies the behavior to log the exception instead of showing the "Report problem UI". + if (IsDwmCompositionException(e as System.Runtime.InteropServices.COMException)) + { + var logger = LogManager.GetLogger(LoggerName); + logger.Error($"From {(isNotUIThread ? "non" : string.Empty)} UI thread's exception: {ExceptionFormatter.FormatException(e)}"); + } + else + { + Report(e, isNotUIThread); + } + } + + private static void Report(Exception e, bool waitForClose) + { + if (e != null) + { + var logger = LogManager.GetLogger(LoggerName); + logger.Fatal($"From {(waitForClose ? "non" : string.Empty)} UI thread's exception: {ExceptionFormatter.FormatException(e)}"); + + var reportWindow = new ReportWindow(e); + + if (waitForClose) + { + reportWindow.ShowDialog(); + } + else + { + reportWindow.Show(); + } + } + } + + private static bool IsDwmCompositionException(System.Runtime.InteropServices.COMException comException) + { + if (comException == null) + { + return false; + } + + var stackTrace = comException.StackTrace; + if (string.IsNullOrEmpty(stackTrace)) + { + return false; + } + + // Check for common DWM composition changed patterns in the stack trace + return stackTrace.Contains("DwmCompositionChanged"); + } } } From 0d29f6aca685d6ed8733a6d4ea045883bb66964c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Jun 2025 13:10:57 +0800 Subject: [PATCH 053/117] Bump check-spelling/check-spelling from 0.0.24 to 0.0.25 (#39572) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [check-spelling/check-spelling](https://github.com/check-spelling/check-spelling) from 0.0.24 to 0.0.25.
Release notes

Sourced from check-spelling/check-spelling's releases.

Release 0.0.25

⏩ Upgrading

  • 🧪 Test first on a branch 🏷️ by changing your workflow tags/references to this release
  • See 🐣 Breaking Changes for how to adapt your workflow
  • See 🐛 Known Issues for known issues

🐣 Breaking Changes

✨ New Features

Dictionaries

Hunspell dictionaries

  • Fix support for .dic/.aff dictionaries by installing hunspell as needed (#79 / #90)
  • Ensure that Spanish works (a consumer is using this, so it should work reliably)

Fixes

  • macOS: Consistently use check-spelling dictionary instead of looking at the system dictionary (#84)
  • Fix check_for_newline_at_eof for allow.txt (#81)
  • Improve handling of inputs.ignored events
  • Fix inputs.debug handling
  • Fix merge instructions order
  • Fix pattern for validating expect entries
  • Fix noisy-file-list handling
  • Fix 504 handling for only_check_changed_files when unshallowing
  • Restore comment (#) support for expect files
  • Include last character in token-is-substring warning

Improvements

  • Line ending detection (#83)
  • RSQM handling
  • Dictionary download times by skipping delays for 30x redirects
  • Excludes paths generation
  • Error handling of various components
  • Documentation links
  • GitHub error detection patterns for when check-spelling has bugs

... (truncated)

Commits
  • c635c2f action: Release v0.0.25
  • a72db74 Yaml: Fix REPORT_MATCHING_YAML=1 handling
  • 7239194 checkout: suppress default branch message
  • 5b54b9b CheckDictionary: Tolerate empty INPUT_IGNORE_PATTERN
  • eedc53a test: Add coverage for non-alpha-in-dictionary
  • 35e2bbc unknown-words: Split error streams
  • b731644 unknown-words: Use encoding(UTF-8)
  • d6cb009 test: Expose dependency timing
  • eac6da3 test: Improve local testing
  • a9ff41e Suggest using # to suppress candidates
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=check-spelling/check-spelling&package-manager=github_actions&previous-version=0.0.24&new-version=0.0.25)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/spelling2.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/spelling2.yml b/.github/workflows/spelling2.yml index 03bb66fff6..cd2b6d5b0f 100644 --- a/.github/workflows/spelling2.yml +++ b/.github/workflows/spelling2.yml @@ -93,7 +93,7 @@ jobs: steps: - name: check-spelling id: spelling - uses: check-spelling/check-spelling@67debf50669c7fc76fc8f5d7f996384535a72b77 # v0.0.24 + uses: check-spelling/check-spelling@c635c2f3f714eec2fcf27b643a1919b9a811ef2e # v0.0.25 with: config: .github/actions/spell-check suppress_push_for_open_pull_request: ${{ github.actor != 'dependabot[bot]' && 1 }} @@ -156,7 +156,7 @@ jobs: if: (success() || failure()) && needs.spelling.outputs.followup && github.event_name == 'push' steps: - name: comment - uses: check-spelling/check-spelling@67debf50669c7fc76fc8f5d7f996384535a72b77 # v0.0.24 + uses: check-spelling/check-spelling@c635c2f3f714eec2fcf27b643a1919b9a811ef2e # v0.0.25 with: config: .github/actions/spell-check checkout: true @@ -175,7 +175,7 @@ jobs: if: (success() || failure()) && needs.spelling.outputs.followup && contains(github.event_name, 'pull_request') steps: - name: comment - uses: check-spelling/check-spelling@67debf50669c7fc76fc8f5d7f996384535a72b77 # v0.0.24 + uses: check-spelling/check-spelling@c635c2f3f714eec2fcf27b643a1919b9a811ef2e # v0.0.25 with: config: .github/actions/spell-check checkout: true @@ -202,7 +202,7 @@ jobs: cancel-in-progress: false steps: - name: apply spelling updates - uses: check-spelling/check-spelling@67debf50669c7fc76fc8f5d7f996384535a72b77 # v0.0.24 + uses: check-spelling/check-spelling@c635c2f3f714eec2fcf27b643a1919b9a811ef2e # v0.0.25 with: experimental_apply_changes_via_bot: ${{ github.repository_owner != 'microsoft' && 1 }} checkout: true From 51b6e41d9353f2601b92076b5259d2aa7013e6ee Mon Sep 17 00:00:00 2001 From: Clint Rutkas Date: Tue, 10 Jun 2025 14:13:25 +0000 Subject: [PATCH 054/117] PowerToys.MonacoPreviewHandler crashes when rendering UTF-8 BOM (#39381) ## Summary of the Pull Request ## PR Checklist - [ ] **Closes:** https://github.com/microsoft/PowerToys/issues/35258 - [ ] **Communication:** I've discussed this with core contributors already. If work hasn't been agreed, this work might be rejected - [ ] **Tests:** Added/updated and all pass - [ ] **Localization:** All end user facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx ## Detailed Description of the Pull Request / Additional comments ## Validation Steps Performed --- .../MonacoPreviewHandler/MonacoPreviewHandlerControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandlerControl.cs b/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandlerControl.cs index 68dde66e3c..c1c72d61aa 100644 --- a/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandlerControl.cs +++ b/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandlerControl.cs @@ -389,7 +389,7 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco } fileReader.Close(); - _base64FileCode = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(fileContent)); + _base64FileCode = Convert.ToBase64String(encodingToUse.GetBytes(fileContent)); Logger.LogInfo("Reading requested file ended"); } From 549ce60483085e3dcdc21d6da271b366a3b43eb0 Mon Sep 17 00:00:00 2001 From: ruslanlap <106077551+ruslanlap@users.noreply.github.com> Date: Tue, 10 Jun 2025 22:08:15 +0300 Subject: [PATCH 055/117] =?UTF-8?q?=F0=9F=9A=80=20Add=20SpeedTest=20Plugin?= =?UTF-8?q?=20for=20PowerToys=20Run=20to=20Third-Party=20plugin=20(#39880)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # 🚀 Add SpeedTest Plugin for PowerToys Run This PR introduces the **SpeedTest** plugin—a new PowerToys Run extension that lets you run internet speed tests instantly, without opening a browser or separate application. Also was problem with formating previous Plugin "Linear" 🔗 **Learn more** | Plugin | Author | Description | |:-------|:------:|:------------| | [SpeedTest](https://github.com/ruslanlap/PowerToysRun-SpeedTest) | [ruslanlap](https://github.com/ruslanlap) | One-command internet speed tests with real-time results, modern UI, and shareable links. | --- ## ✨ Plugin Highlights - **One-command tests** Run a full speed test (download, upload, ping, server) via PowerToys Run (). and hit Enter! (or change to or any other keyword in the settings) - **Modern, theme-aware UI** WPF-based window adapts automatically to light or dark Windows modes. - **Live progress & shareable links** Watch your test run in real time and copy a result URL when it finishes. - **All-local, privacy-first** No third-party tracking—uses the bundled Speedtest CLI for 100% local execution. - **Robust error handling** Friendly messages for network failures, permission issues, or unexpected errors. - **x64 & ARM64 builds** Native-optimized releases for both CPU architectures. - **Open source & community-driven** Contributions, issues, and feature requests are welcome—let's make it even better! --- Thank you for reviewing the **SpeedTest** plugin! 🚀 I'm excited to help PowerToys Run users check their internet speeds faster than ever. --- doc/thirdPartyRunPlugins.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/thirdPartyRunPlugins.md b/doc/thirdPartyRunPlugins.md index ec8ffc69a4..1dca8f50af 100644 --- a/doc/thirdPartyRunPlugins.md +++ b/doc/thirdPartyRunPlugins.md @@ -68,4 +68,5 @@ Below are community created plugins that target a website or software. They are | [Bilibili](https://github.com/Whuihuan/PowerToysRun-Bilibili) | [Whuihuan](https://github.com/Whuihuan) | Use AVID or BVID to parse and jump to Bilibili | | [YubicoOauthOTP](https://github.com/dlnilsson/Community.PowerToys.Run.Plugin.YubicoOauthOTP) | [dlnilsson](https://github.com/dlnilsson) | Display generated codes from OATH accounts stored on the YubiKey in powerToys Run | | [Firefox Bookmark](https://github.com/8LWXpg/PowerToysRun-FirefoxBookmark) | [8LWXpg](https://github.com/8LWXpg) | Open bookmarks in Firefox based browser | -[Linear](https://github.com/vednig/powertoys-linear) | [vednig](https://github.com/vednig) | Create Linear Issues directly from Powertoys Run | +| [Linear](https://github.com/vednig/powertoys-linear) | [vednig](https://github.com/vednig) | Create Linear Issues directly from Powertoys Run | +| [SpeedTest](https://github.com/ruslanlap/PowerToysRun-SpeedTest) | [ruslanlap](https://github.com/ruslanlap) | One-command internet speed tests with real-time results, modern UI, and shareable links. | From e14ace4bfc545787216d1f3a0d6cefd5456dd3a8 Mon Sep 17 00:00:00 2001 From: ruslanlap <106077551+ruslanlap@users.noreply.github.com> Date: Tue, 10 Jun 2025 22:14:50 +0300 Subject: [PATCH 056/117] =?UTF-8?q?=F0=9F=93=9A=20Add=20Definition=20Plugi?= =?UTF-8?q?n=20for=20PowerToys=20Run=20(#39881)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR introduces the **Definition** plugin—a new PowerToys Run extension that allows you to quickly lookup word definitions, phonetics, and synonyms without leaving your keyboard. 🔗 **Learn more** | Plugin | Author | Description | |:-------|:------:|:------------| | [Definition](https://github.com/ruslanlap/PowerToysRun-Definition) | [ruslanlap](https://github.com/ruslanlap) | Lookup word definitions, phonetics, and synonyms without leaving your keyboard. | --- ## ✨ Features - **Instant Definitions** Get definitions in real-time via dictionaryapi.dev. - **Pronunciation Audio** Play phonetic audio directly from your results. - **Phonetics & Synonyms** View phonetic spelling, synonyms, and antonyms. - **Usage Examples** See real-world examples of how words are used. - **Delayed Execution** Shows loading indicator before fetching results. - **Caching** In-memory cache for repeat lookups (up to 100 entries) to improve performance. - **Theme Awareness** Automatically switches icons for light/dark mode. - **Context Menu** Copy definitions, play pronunciation, open source URL, or search for related words via right-click or keyboard shortcuts. - **Cancellable Requests** Automatically cancels previous requests when typing new queries. - **Wiktionary Integration** Open any word in Wiktionary for additional information and translations. --- Thank you for reviewing the **Definition** plugin! 📚 I'm excited to help PowerToys Run users look up words faster than ever. --- doc/thirdPartyRunPlugins.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/thirdPartyRunPlugins.md b/doc/thirdPartyRunPlugins.md index 1dca8f50af..19c516c68b 100644 --- a/doc/thirdPartyRunPlugins.md +++ b/doc/thirdPartyRunPlugins.md @@ -46,6 +46,7 @@ Contact the developers of a plugin directly for assistance with a specific plugi | [QuickNotes](https://github.com/ruslanlap/CommunityPowerToysRunPlugin-QuickNotes) | [ruslanlap](https://github.com/ruslanlap) | Create, manage, and search notes directly from PowerToys Run. | | [Weather](https://github.com/ruslanlap/PowerToysRun-Weather) | [ruslanlap](https://github.com/ruslanlap) | Get real-time weather information directly from PowerToys Run. | | [Pomodoro](https://github.com/ruslanlap/PowerToysRun-Pomodoro) | [ruslanlap](https://github.com/ruslanlap) | Manage Pomodoro productivity sessions directly from PowerToys Run. | +| [Definition](https://github.com/ruslanlap/PowerToysRun-Definition) | [ruslanlap](https://github.com/ruslanlap) | Lookup word definitions, phonetics, and synonyms directly in PowerToys Run. | ## Extending software plugins From 13a7ceba6d466b1df3064cf412289788a882a91d Mon Sep 17 00:00:00 2001 From: SkandaBT <9980056379Skanda@gmail.com> Date: Wed, 11 Jun 2025 00:47:38 +0530 Subject: [PATCH 057/117] Update documentation (#39603) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary of the Pull Request This PR applies a series of grammar, punctuation, and stylistic updates across the main README and GitHub templates to improve consistency, clarity, and alignment with formatting guidelines. ### File-Level Changes | Change | Details | Files | | ------ | ------- | ----- | | Updated README.md for grammar, punctuation, and phrasing consistency |
  • Inserted missing commas and adjusted articles for clearer sentences
  • Standardized hyphenation and removed extra spaces around slashes
  • Refined bullet list punctuation and corrected pluralization (‘number of bugs’)
| `README.md` | | Refined pull request template text for clarity and uniform formatting |
  • Hyphenated “end-user-facing” and added Oxford comma in checklist items
  • Adjusted phrasing in communication bullet for parallel structure
  • Reworded placeholder comment for detailed PR description
| `.github/pull_request_template.md` | | Standardized issue templates with grammar and placeholder improvements |
  • Clarified label descriptions and added missing commas in placeholders
  • Unified question phrasing across bug, feature, translation, and docs templates
  • Corrected capitalization and refined instructions for consistency
| `.github/ISSUE_TEMPLATE/bug_report.yml`
`.github/ISSUE_TEMPLATE/feature_request.yml`
`.github/ISSUE_TEMPLATE/translation_issue.yml`
`.github/ISSUE_TEMPLATE/documentation-issue.yml` | --- ## PR Checklist - [ ] **Closes:** #xxx - [X] **Communication:** I've already discussed this with core contributors. If the work hasn't been agreed, this work might be rejected - [X] **Tests:** Added/updated and all pass - [ ] **Localization:** All end-user-facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [X] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: https://github.com/MicrosoftDocs/windows-dev-docs/pull/5447 ## Detailed Description of the Pull Request / Additional comments ## Validation Steps Performed --------- Co-authored-by: Jay <65828559+Jay-o-Way@users.noreply.github.com> --- .github/ISSUE_TEMPLATE/bug_report.yml | 4 +-- .../ISSUE_TEMPLATE/documentation-issue.yml | 2 +- .github/ISSUE_TEMPLATE/feature_request.yml | 2 +- .github/ISSUE_TEMPLATE/translation_issue.yml | 4 +-- .github/pull_request_template.md | 6 ++-- README.md | 28 +++++++++---------- 6 files changed, 23 insertions(+), 23 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index e55d081c91..5e5ea03e7e 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -12,7 +12,7 @@ body: attributes: label: Microsoft PowerToys version placeholder: X.XX.X - description: Hover over system tray icon or look at Settings + description: Hover over the system tray icon or look at Settings validations: required: true @@ -20,7 +20,7 @@ body: type: dropdown attributes: label: Installation method - description: How / Where was PowerToys installed from? + description: How / where was PowerToys installed from? multiple: true options: - GitHub diff --git a/.github/ISSUE_TEMPLATE/documentation-issue.yml b/.github/ISSUE_TEMPLATE/documentation-issue.yml index 151fc5a1f7..583cf54811 100644 --- a/.github/ISSUE_TEMPLATE/documentation-issue.yml +++ b/.github/ISSUE_TEMPLATE/documentation-issue.yml @@ -6,7 +6,7 @@ labels: body: - type: textarea attributes: - label: Provide a description of requested docs changes + label: Describe the requested doc changes placeholder: Briefly describe which document needs to be corrected and why. validations: required: true diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index d7d092dbca..a2c7db9cc5 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -13,7 +13,7 @@ body: - type: textarea attributes: label: Scenario when this would be used? - placeholder: What is the scenario this would be used? Why is this important to your workflow as a power user? + placeholder: What is the scenario this would be used in? Why is this important to your workflow as a power user? validations: required: true - type: textarea diff --git a/.github/ISSUE_TEMPLATE/translation_issue.yml b/.github/ISSUE_TEMPLATE/translation_issue.yml index ffddacb9aa..1fdefbff8b 100644 --- a/.github/ISSUE_TEMPLATE/translation_issue.yml +++ b/.github/ISSUE_TEMPLATE/translation_issue.yml @@ -14,7 +14,7 @@ body: attributes: label: Microsoft PowerToys version placeholder: 0.70.0 - description: Hover over system tray icon or look at Settings + description: Hover over the system tray icon or look at Settings validations: required: true - type: dropdown @@ -65,7 +65,7 @@ body: - type: textarea attributes: label: ❌ Actual phrase(s) - placeholder: What is there? Please include a screenshot as that is extremely helpful. + placeholder: What is there? Please include a screenshot, as that is extremely helpful. validations: required: true - type: textarea diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 0cff106acb..2db246d63b 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -5,9 +5,9 @@ ## PR Checklist - [ ] **Closes:** #xxx -- [ ] **Communication:** I've discussed this with core contributors already. If work hasn't been agreed, this work might be rejected +- [ ] **Communication:** I've discussed this with core contributors already. If the work hasn't been agreed, this work might be rejected - [ ] **Tests:** Added/updated and all pass -- [ ] **Localization:** All end user facing strings can be localized +- [ ] **Localization:** All end-user-facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries @@ -16,7 +16,7 @@ - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx - + ## Detailed Description of the Pull Request / Additional comments diff --git a/README.md b/README.md index 44c1a800c0..232cf6fd82 100644 --- a/README.md +++ b/README.md @@ -53,10 +53,10 @@ This is our preferred method. ### Via Microsoft Store -Install from the [Microsoft Store's PowerToys page][microsoft-store-link]. You must be using the [new Microsoft Store](https://blogs.windows.com/windowsExperience/2021/06/24/building-a-new-open-microsoft-store-on-windows-11/) which is available for both Windows 11 and Windows 10. +Install from the [Microsoft Store's PowerToys page][microsoft-store-link]. You must be using the [new Microsoft Store](https://blogs.windows.com/windowsExperience/2021/06/24/building-a-new-open-microsoft-store-on-windows-11/), which is available for both Windows 11 and Windows 10. ### Via WinGet -Download PowerToys from [WinGet][winget-link]. Updating PowerToys via winget will respect current PowerToys installation scope. To install PowerToys, run the following command from the command line / PowerShell: +Download PowerToys from [WinGet][winget-link]. Updating PowerToys via winget will respect the current PowerToys installation scope. To install PowerToys, run the following command from the command line / PowerShell: #### User scope installer [default] ```powershell @@ -79,7 +79,7 @@ There is a collection of [third-party plugins](./doc/thirdPartyRunPlugins.md) cr ## Contributing -This project welcomes contributions of all types. Besides coding features / bug fixes, other ways to assist include spec writing, design, documentation, and finding bugs. We are excited to work with the power user community to build a set of tools for helping you get the most out of Windows. +This project welcomes contributions of all types. Besides coding features / bug fixes, other ways to assist include spec writing, design, documentation, and finding bugs. We are excited to work with the power user community to build a set of tools for helping you get the most out of Windows. We ask that **before you start work on a feature that you would like to contribute**, please read our [Contributor's Guide](CONTRIBUTING.md). We would be happy to work with you to figure out the best approach, provide guidance and mentorship throughout feature development, and help avoid any wasted or duplicate effort. @@ -99,7 +99,7 @@ In this release, we focused on new features, stability, and automation. **✨Highlights** - - We focused on greatly improving Command Palette's performance and fixing a large amount of bugs. Some new features we've added are: + - We focused on greatly improving the Command Palette's performance and fixing a large number of bugs. Some new features we've added are: - Added the ability for Command Palette to search any file using a fallback command. - Added the ability to make the Command Palette global hotkey a low-level keyboard hook. - Added open URL fallback command for the WebSearch extension, enabling users to directly open URLs in the browser from Command Palette. @@ -117,17 +117,17 @@ In this release, we focused on new features, stability, and automation. ### Command Not Found - - Updated the WinGet Command Not Found script to only enable the experimental features if they actually exist. + - Updated the WinGet Command Not Found script to only enable the experimental features if they exist. ### Command Palette - Updated bug template to include Command Palette module. - Fixed an issue where the toast window was not scaled for DPI, causing layout issues under display scaling. - - Fixed an issue where Up/Down keyboard navigation didn't move selection when caret was at position 0, and add continuous navigation like PT Run v1. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! + - Fixed an issue where Up/Down keyboard navigation didn't move selection when the caret was at position 0, and added continuous navigation like PT Run v1. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! - Updated the Time and Date extension code to simplify it and improve clarity. - Fixed an issue where capitalization in the command causes failure when trying to go to the mouse pointer, resolved by adjusting the command to lowercase. - - Added open URL fallback command for the WebSearch extension, enabling users to directly open URLs in the browser from Command Palette. Thanks [@htcfreek](https://github.com/htcfreek)! - - Added setting to enable/disable system tray icon in CmdPal and align terminology with Windows 11. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! + - Added open URL fallback command for the WebSearch extension, enabling users to directly open URLs in the browser from the Command Palette. Thanks [@htcfreek](https://github.com/htcfreek)! + - Added setting to enable/disable system tray icon in CmdPal and aligned terminology with Windows 11. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! - Fixed an alias update issue by removing the old alias when a new one is set. - Resolved GitHub casing conflict by migrating Exts and exts into a new ext directory, ensuring consistent structure across platforms and preventing path fragmentation. - Fix an issue where the 'Create New Extension' command generated empty file names. @@ -155,9 +155,9 @@ In this release, we focused on new features, stability, and automation. - Refactored CmdPal classes to improve JSON serialization and introduced new serialization contexts for better performance and maintainability. - Added support for ahead-of-time (AoT) compilation. - Added retry mechanism for CmdPal launch. - - Removed some unused files from CmdPal.Common to simplify codebase and facilitate marking it as AoT-compatible. + - Removed some unused files from CmdPal.Common to simplify the codebase and facilitate marking it as AoT-compatible. - Fixed a bug where a race condition in the update of SearchText caused the cursor in the input box to automatically jump to the end of the line, ensuring SearchText is only updated after it has actually been changed. - - Added support for searching any file in fallback command. + - Added support for searching any file in the fallback command. - Cleaned up AoT-related code to prevent duplicate operations during testing. - Reduced CmdPal load time by parallelizing extension startup and adding timeouts to prevent misbehaving extensions from blocking others. - Enhanced UI behavior by dismissing the details pane when the list gets emptied, avoiding inconsistent visual states. @@ -179,7 +179,7 @@ In this release, we focused on new features, stability, and automation. ### Keyboard Manager - - Fixed an issue where a modifier key, when set without specifying left or right, would get stuck due to incorrect key handling, by tracking the pressed keys and sending the correct key accordingly. Thanks [@mantaionut](https://github.com/mantaionut)! + - Fixed an issue where a modifier key, when set without specifying left or right, would get stuck due to incorrect key handling by tracking the pressed keys and sending the correct key accordingly. Thanks [@mantaionut](https://github.com/mantaionut)! ### PowerRename @@ -187,7 +187,7 @@ In this release, we focused on new features, stability, and automation. ### PowerToys Run - - Added support for custom formats in the "Time and Date" plugin and improves error messages for invalid input formats. Thanks [@htcfreek](https://github.com/htcfreek)! + - Added support for custom formats in the "Time and Date" plugin and improved error messages for invalid input formats. Thanks [@htcfreek](https://github.com/htcfreek)! - Fix two crashes: one for WFT on very early dates and another for calculating the week of the month on very late dates (e.g., 31.12.9999), and reorder UI settings. Thanks [@htcfreek](https://github.com/htcfreek)! - Fix an issue where capitalization in the command causes failure when trying to go to the mouse pointer, resolved by adjusting the command to lowercase. - Added version details to plugin error messages for 'Loading error' and 'Init error'. Thanks [@htcfreek](https://github.com/htcfreek)! @@ -254,7 +254,7 @@ For [v0.92][github-next-release-work], we'll work on the items below: - New UI Automation tests - Working on installer upgrades - Upgrading Keyboard Manager's editor UI - - Stability / bug fixes + - Stability, bug fixes ## PowerToys Community @@ -266,7 +266,7 @@ This project has adopted the [Microsoft Open Source Code of Conduct][oss-conduct ## Privacy Statement -The application logs basic diagnostic data (telemetry). For more information on privacy and what we collect, see our [PowerToys Data and Privacy documentation](https://aka.ms/powertoys-data-and-privacy-documentation). +The application logs basic diagnostic data (telemetry). For more privacy information and what we collect, see our [PowerToys Data and Privacy documentation](https://aka.ms/powertoys-data-and-privacy-documentation). [oss-CLA]: https://cla.opensource.microsoft.com [oss-conduct-code]: CODE_OF_CONDUCT.md From 55b9b523494d868bbb9410d3f902c72e481f83e4 Mon Sep 17 00:00:00 2001 From: Jeremy Sinclair <4016293+snickler@users.noreply.github.com> Date: Wed, 11 Jun 2025 01:43:53 -0400 Subject: [PATCH 058/117] [Deps] Update .NET packages from 9.0.5 to 9.0.6 (#39986) ## Summary of the Pull Request Updates .NET 9 Runtime / Library packages to the latest 9.0.6 servicing release for security fixes. ## PR Checklist - [ ] **Closes:** #xxx - [ ] **Communication:** I've discussed this with core contributors already. If the work hasn't been agreed, this work might be rejected - [ ] **Tests:** Added/updated and all pass - [ ] **Localization:** All end-user-facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx ## Detailed Description of the Pull Request / Additional comments This PR also updates the version of System.Text.Json to 9.0.6 in the CmdPal extension template. ## Validation Steps Performed --------- Co-authored-by: Gordon Lam (SH) --- Directory.Packages.props | 44 +++++++++---------- NOTICE.md | 44 +++++++++---------- .../Directory.Packages.props | 2 +- 3 files changed, 45 insertions(+), 45 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index a4ac308b23..8116fe99b6 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -31,22 +31,22 @@ - + - + - - - - - + + + + + - + - + - + - - - + + + - + - + - - + + - + - - - - + + + + diff --git a/NOTICE.md b/NOTICE.md index 9fb3dda87d..b44a8cc788 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -1453,22 +1453,22 @@ SOFTWARE. - Mages 3.0.0 - Markdig.Signed 0.34.0 - MessagePack 3.1.3 -- Microsoft.Bcl.AsyncInterfaces 9.0.5 +- Microsoft.Bcl.AsyncInterfaces 9.0.6 - Microsoft.CodeAnalysis.NetAnalyzers 9.0.0 -- Microsoft.Data.Sqlite 9.0.5 +- Microsoft.Data.Sqlite 9.0.6 - Microsoft.Diagnostics.Tracing.TraceEvent 3.1.16 - Microsoft.DotNet.ILCompiler (A) -- Microsoft.Extensions.DependencyInjection 9.0.5 -- Microsoft.Extensions.Hosting 9.0.5 -- Microsoft.Extensions.Hosting.WindowsServices 9.0.5 -- Microsoft.Extensions.Logging 9.0.5 -- Microsoft.Extensions.Logging.Abstractions 9.0.5 +- Microsoft.Extensions.DependencyInjection 9.0.6 +- Microsoft.Extensions.Hosting 9.0.6 +- Microsoft.Extensions.Hosting.WindowsServices 9.0.6 +- Microsoft.Extensions.Logging 9.0.6 +- Microsoft.Extensions.Logging.Abstractions 9.0.6 - Microsoft.NET.ILLink.Tasks (A) - Microsoft.SemanticKernel 1.15.0 - Microsoft.Toolkit.Uwp.Notifications 7.1.2 - Microsoft.Web.WebView2 1.0.2903.40 -- Microsoft.Win32.SystemEvents 9.0.5 -- Microsoft.Windows.Compatibility 9.0.5 +- Microsoft.Win32.SystemEvents 9.0.6 +- Microsoft.Windows.Compatibility 9.0.6 - Microsoft.Windows.CsWin32 0.2.46-beta - Microsoft.Windows.CsWinRT 2.2.0 - Microsoft.Windows.SDK.BuildTools 10.0.22621.2428 @@ -1487,25 +1487,25 @@ SOFTWARE. - SharpCompress 0.37.2 - StreamJsonRpc 2.21.69 - StyleCop.Analyzers 1.2.0-beta.556 -- System.CodeDom 9.0.5 +- System.CodeDom 9.0.6 - System.CommandLine 2.0.0-beta4.22272.1 -- System.ComponentModel.Composition 9.0.5 -- System.Configuration.ConfigurationManager 9.0.5 -- System.Data.OleDb 9.0.5 -- System.Data.SqlClient 4.8.6 -- System.Diagnostics.EventLog 9.0.5 -- System.Diagnostics.PerformanceCounter 9.0.5 -- System.Drawing.Common 9.0.5 +- System.ComponentModel.Composition 9.0.6 +- System.Configuration.ConfigurationManager 9.0.6 +- System.Data.OleDb 9.0.6 +- System.Data.SqlClient 4.9.0 +- System.Diagnostics.EventLog 9.0.6 +- System.Diagnostics.PerformanceCounter 9.0.6 +- System.Drawing.Common 9.0.6 - System.IO.Abstractions 22.0.13 - System.IO.Abstractions.TestingHelpers 22.0.13 -- System.Management 9.0.5 +- System.Management 9.0.6 - System.Net.Http 4.3.4 - System.Private.Uri 4.3.2 - System.Reactive 6.0.1 -- System.Runtime.Caching 9.0.5 -- System.ServiceProcess.ServiceController 9.0.5 -- System.Text.Encoding.CodePages 9.0.5 -- System.Text.Json 9.0.5 +- System.Runtime.Caching 9.0.6 +- System.ServiceProcess.ServiceController 9.0.6 +- System.Text.Encoding.CodePages 9.0.6 +- System.Text.Json 9.0.6 - System.Text.RegularExpressions 4.3.1 - UnicodeInformation 2.6.0 - UnitsNet 5.56.0 diff --git a/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/Directory.Packages.props b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/Directory.Packages.props index cf286b3822..2439092e9c 100644 --- a/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/Directory.Packages.props +++ b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/Directory.Packages.props @@ -12,6 +12,6 @@ - +
From b9aac70de52f41a9371836235241be0107653260 Mon Sep 17 00:00:00 2001 From: rovercoder Date: Wed, 11 Jun 2025 18:32:38 +0200 Subject: [PATCH 059/117] Spell Checking Fixes (#39985) ## Summary of the Pull Request Fixes spelling issues and spell checker warnings --- .github/actions/spell-check/excludes.txt | 2 ++ .github/actions/spell-check/expect.txt | 21 +------------------ .github/actions/spell-check/patterns.txt | 2 +- .../Controls/ContentFormControl.xaml.cs | 2 +- 4 files changed, 5 insertions(+), 22 deletions(-) diff --git a/.github/actions/spell-check/excludes.txt b/.github/actions/spell-check/excludes.txt index 21d7da66b8..f7fb1ec196 100644 --- a/.github/actions/spell-check/excludes.txt +++ b/.github/actions/spell-check/excludes.txt @@ -92,6 +92,7 @@ ^\.github/actions/spell-check/ ^\.gitmodules$ ^\Q.github/workflows/spelling2.yml\E$ +^\Q.pipelines/272MSSharedLibSN2048.snk\E$ ^\Q.pipelines/ESRPSigning_core.json\E$ ^\Qdoc/devdocs/localization.md\E$ ^\Qsrc/common/ManagedCommon/ColorFormatHelper.cs\E$ @@ -120,6 +121,7 @@ ^src/modules/MouseWithoutBorders/App/Form/.*\.resx$ ^src/modules/MouseWithoutBorders/App/Helper/.*\.resx$ ^src/modules/previewpane/UnitTests-MarkdownPreviewHandler/HelperFiles/MarkdownWithHTMLImageTag\.txt$ +^src/modules/ZoomIt/ZoomIt/ZoomIt\.idc$ ^src/Monaco/ ^src/common/sysinternals/Eula/ ^tools/Verification scripts/Check preview handler registration\.ps1$ diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index ea369d57af..a7012852e7 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -328,7 +328,6 @@ DEFAULTTONULL DEFAULTTOPRIMARY DEFERERASE DEFPUSHBUTTON -DEFT deinitialization DELA DELETEDKEYIMAGE @@ -670,7 +669,6 @@ IBeam icf ICONERROR ICONLOCATION -idc IDCANCEL IDD idk @@ -681,7 +679,6 @@ IDR IDXGI ietf IEXPLORE -iextn IFACEMETHOD IFACEMETHODIMP IFile @@ -720,7 +717,6 @@ INPUTMOUSE INPUTSINK INPUTTYPE INSTALLDESKTOPSHORTCUT -INSTALLDIR installdir INSTALLFOLDER INSTALLFOLDERTOBOOTSTRAPPERINSTALLFOLDER @@ -812,7 +808,6 @@ LMENU lnks LOADFROMFILE LOBYTE -localappdata LOCALDISPLAY localpackage LOCALSYSTEM @@ -977,7 +972,6 @@ msrc msstore mst msvcp -msvsmon MTND MULTIPLEUSE multizone @@ -1284,7 +1278,6 @@ pstm PStr pstream pstrm -pswd PSYSTEM psz ptb @@ -1476,16 +1469,12 @@ SHELLDLL shellex SHELLEXECUTEINFO SHELLEXECUTEINFOW -SHELLEXTENSION SHELLICONSIZE -SHELLNEWVALUE SHFILEINFO SHFILEOPSTRUCT SHGDN SHGDNF SHGFI -SHGFIICON -SHGFILARGEICON SHIL shinfo shlwapi @@ -1519,7 +1508,6 @@ siex sigdn SIGNINGSCENARIO signtool -Signtool SINGLEKEY sipolicy SIZEBOX @@ -1695,7 +1683,6 @@ TLayout tlb tlbimp tlc -TGM TNP Toolhelp toolkitconverters @@ -1783,7 +1770,6 @@ uxtheme vabdq validmodulename valuegenerator -VARENUM variantassignment vcamp VCENTER @@ -2003,11 +1989,6 @@ CLSCTXINPROCALL IIDI irow lcid -OTHERUNZOOM -OTHERZOOM -PARENTCLOSING -PARENTOPENING ppwsz rguid -SCROLLCHILDREN -VARTYPE \ No newline at end of file +VARTYPE diff --git a/.github/actions/spell-check/patterns.txt b/.github/actions/spell-check/patterns.txt index 5a6f4785b1..6bd99fb83e 100644 --- a/.github/actions/spell-check/patterns.txt +++ b/.github/actions/spell-check/patterns.txt @@ -243,4 +243,4 @@ Process Process # ZoomIt menu items with accelerator keys E&xit -St&yle \ No newline at end of file +St&yle diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/ContentFormControl.xaml.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/ContentFormControl.xaml.cs index b2afe78dfb..78805f00b2 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/ContentFormControl.xaml.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/ContentFormControl.xaml.cs @@ -18,7 +18,7 @@ public sealed partial class ContentFormControl : UserControl // LOAD-BEARING: if you don't hang onto a reference to the RenderedAdaptiveCard // then the GC might clean it up sometime, even while the card is in the UI - // tree. If this gets GC'd, then it'll revoke our Action handler, and the + // tree. If this gets GC'ed, then it'll revoke our Action handler, and the // form will do seemingly nothing. private RenderedAdaptiveCard? _renderedCard; From a9c5117f618d19fe796eaab17e686408756e444b Mon Sep 17 00:00:00 2001 From: Dave Rayment Date: Thu, 12 Jun 2025 00:08:23 +0100 Subject: [PATCH 060/117] Command Palette: fix sample links in README.md (#39983) Fix links to sample folders. ## Summary of the Pull Request Two links were broken in the Command Palette readme because of a folder name being "Exts" instead of "ext". ## PR Checklist - [ ] **Closes:** #xxx - [ ] **Communication:** I've discussed this with core contributors already. If work hasn't been agreed, this work might be rejected - [ ] **Tests:** Added/updated and all pass - [ ] **Localization:** All end user facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx ## Detailed Description of the Pull Request / Additional comments N/A ## Validation Steps Performed Manually confirmed the following links are correct: https://github.com/microsoft/PowerToys/tree/main/src/modules/cmdpal/ext/SamplePagesExtension https://github.com/microsoft/PowerToys/tree/main/src/modules/cmdpal/ext/ProcessMonitorExtension --- src/modules/cmdpal/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/cmdpal/README.md b/src/modules/cmdpal/README.md index d0c626e3f1..b9e0a42f61 100644 --- a/src/modules/cmdpal/README.md +++ b/src/modules/cmdpal/README.md @@ -39,8 +39,8 @@ Projects of interest are: [Initial SDK Spec]: ./doc/initial-sdk-spec/initial-sdk-spec.md -[generic samples]: ./Exts/SamplePagesExtension -[real samples]: ./Exts/ProcessMonitorExtension +[generic samples]: ./ext/SamplePagesExtension +[real samples]: ./ext/ProcessMonitorExtension [real extensions that we've "shipped" already]: https://github.com/zadjii/CmdPalExtensions/blob/main/src/extensions From e9a79f5d6f3fe62a9b6f1c27c0fe41851fb84739 Mon Sep 17 00:00:00 2001 From: Gordon Lam <73506701+yeelam-gordon@users.noreply.github.com> Date: Wed, 11 Jun 2025 18:15:25 -0700 Subject: [PATCH 061/117] Add the detail section (#39991) ## Summary of the Pull Request The `verifyNoticeMdAgainstNugetPackages.ps1` script previously only reported "Notice.md does not match NuGet list." without providing any details about which packages were different, making it difficult to debug discrepancies. ## Changes Made This enhancement adds detailed difference reporting when NuGet packages don't match: - **Extracts current package list from NOTICE.md** using regex pattern matching - **Shows packages missing from NOTICE.md** (highlighted in red) - **Shows packages in NOTICE.md but not in generated list** (highlighted in yellow) - **Provides summary statistics** with package counts - **Maintains backward compatibility** - all existing functionality preserved ## PR Checklist If there is no difference, the same behaviour. If there is difference, here is the example: ![image](https://github.com/user-attachments/assets/63dad21a-9db5-4d20-8a2c-ddd44ce1ee80) ## Edge Cases Handled - **Missing NuGet section**: Shows warning and treats as empty package list - **Empty package lists**: Handles gracefully with appropriate counts - **Matching lists**: No additional output (preserves existing behavior) This enhancement significantly improves the debugging experience when NuGet package verification fails by providing specific, actionable information about which packages need attention. --- .../verifyNoticeMdAgainstNugetPackages.ps1 | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/.pipelines/verifyNoticeMdAgainstNugetPackages.ps1 b/.pipelines/verifyNoticeMdAgainstNugetPackages.ps1 index ebef8412a7..e3120836c8 100644 --- a/.pipelines/verifyNoticeMdAgainstNugetPackages.ps1 +++ b/.pipelines/verifyNoticeMdAgainstNugetPackages.ps1 @@ -72,9 +72,57 @@ $returnList = [System.Collections.Generic.HashSet[string]]($totalList) -join "`r Write-Host $returnList +# Extract the current package list from NOTICE.md +$noticePattern = "## NuGet Packages used by PowerToys\s*((?:\r?\n- .+)+)" +$noticeMatch = [regex]::Match($noticeFile, $noticePattern) + +if ($noticeMatch.Success) { + $currentNoticePackageList = $noticeMatch.Groups[1].Value.Trim() +} else { + Write-Warning "Warning: Could not find 'NuGet Packages used by PowerToys' section in NOTICE.md" + $currentNoticePackageList = "" +} + if (!$noticeFile.Trim().EndsWith($returnList.Trim())) { Write-Host -ForegroundColor Red "Notice.md does not match NuGet list." + + # Show detailed differences + $generatedPackages = $returnList -split "`r`n|`n" | Where-Object { $_.Trim() -ne "" } | Sort-Object + $noticePackages = $currentNoticePackageList -split "`r`n|`n" | Where-Object { $_.Trim() -ne "" } | ForEach-Object { $_.Trim() } | Sort-Object + + Write-Host "" + Write-Host -ForegroundColor Cyan "=== DETAILED DIFFERENCE ANALYSIS ===" + Write-Host "" + + # Find packages in proj file list but not in NOTICE.md + $missingFromNotice = $generatedPackages | Where-Object { $noticePackages -notcontains $_ } + if ($missingFromNotice.Count -gt 0) { + Write-Host -ForegroundColor Red "MissingFromNotice:" + foreach ($pkg in $missingFromNotice) { + Write-Host -ForegroundColor Red " $pkg" + } + Write-Host "" + } + + # Find packages in NOTICE.md but not in proj file list + $extraInNotice = $noticePackages | Where-Object { $generatedPackages -notcontains $_ } + if ($extraInNotice.Count -gt 0) { + Write-Host -ForegroundColor Yellow "ExtraInNotice:" + foreach ($pkg in $extraInNotice) { + Write-Host -ForegroundColor Yellow " $pkg" + } + Write-Host "" + } + + # Show counts for summary + Write-Host -ForegroundColor Cyan "Summary:" + Write-Host " Proj file list has $($generatedPackages.Count) packages" + Write-Host " NOTICE.md has $($noticePackages.Count) packages" + Write-Host " MissingFromNotice: $($missingFromNotice.Count) packages" + Write-Host " ExtraInNotice: $($extraInNotice.Count) packages" + Write-Host "" + exit 1 } From 3f834d7a6b078e448ba99949c4c278c28f18765e Mon Sep 17 00:00:00 2001 From: Kai Tao <69313318+vanzue@users.noreply.github.com> Date: Thu, 12 Jun 2025 09:44:32 +0800 Subject: [PATCH 062/117] Build: Change CLR projects to portable type pdb (#39992) ## Summary of the Pull Request Since now githubcopilot is easier to used in vscode, would like to set up debug in vscode. Full type pdb can't be loaded in visual studio code debugging Change the debugging type to make it debuggable from vscode. image from: https://github.com/dotnet/core/blob/e6049bb60307d987e044c39a106e0d6cf98857a3/Documentation/diagnostics/portable_pdb.md ## PR Checklist - [ ] **Closes:** #xxx - [ ] **Communication:** I've discussed this with core contributors already. If the work hasn't been agreed, this work might be rejected - [ ] **Tests:** Added/updated and all pass - [ ] **Localization:** All end-user-facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx ## Detailed Description of the Pull Request / Additional comments ## Validation Steps Performed Can debug settings in vscode. --- src/Common.Dotnet.CsWinRT.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Common.Dotnet.CsWinRT.props b/src/Common.Dotnet.CsWinRT.props index d37c39685a..300041b168 100644 --- a/src/Common.Dotnet.CsWinRT.props +++ b/src/Common.Dotnet.CsWinRT.props @@ -22,7 +22,7 @@ true DEBUG;TRACE - full + portable false @@ -43,4 +43,4 @@    - \ No newline at end of file + From 6e0f1819d5ef5afe478caece261511fdf50ec684 Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Wed, 11 Jun 2025 20:53:40 -0500 Subject: [PATCH 063/117] build: adjust for changes in the Az.Accounts module (#40001) They made secure strings the default. Doing it this way maintains compatibility with the version before and after the default changed. This fixes symbol publication. --- .../job-publish-symbols-using-symbolrequestprod-api.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pipelines/v2/templates/job-publish-symbols-using-symbolrequestprod-api.yml b/.pipelines/v2/templates/job-publish-symbols-using-symbolrequestprod-api.yml index 967b7ba4eb..6b214be612 100644 --- a/.pipelines/v2/templates/job-publish-symbols-using-symbolrequestprod-api.yml +++ b/.pipelines/v2/templates/job-publish-symbols-using-symbolrequestprod-api.yml @@ -68,7 +68,7 @@ jobs: pwsh: true ScriptType: InlineScript Inline: |- - $AzToken = (Get-AzAccessToken -ResourceUrl api://30471ccf-0966-45b9-a979-065dbedb24c1).Token + $AzToken = (Get-AzAccessToken -AsSecureString -ResourceUrl api://30471ccf-0966-45b9-a979-065dbedb24c1).Token | ConvertFrom-SecureString -AsPlainText Write-Host "##vso[task.setvariable variable=SymbolAccessToken;issecret=true]$AzToken" From e9dbcbaebb6e56bd44d64989be67c113c64e2ef4 Mon Sep 17 00:00:00 2001 From: Jakub Marcowski <37378746+Chubercik@users.noreply.github.com> Date: Thu, 12 Jun 2025 12:38:04 +0200 Subject: [PATCH 064/117] cziplib: Update to 0.3.3 (#39604) ## Summary of the Pull Request Updated the `zip` dependency to the latest stable version ([0.3.3](https://github.com/kuba--/zip/releases/tag/v0.3.3)). --- NOTICE.md | 37 ++++++++++++++++--------------------- deps/cziplib | 2 +- 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/NOTICE.md b/NOTICE.md index b44a8cc788..fb2450b66a 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -807,30 +807,25 @@ DEALINGS IN THE SOFTWARE. **Source**: https://github.com/kuba--/zip -This is free and unencumbered software released into the public domain. +All Rights Reserved. -Anyone is free to copy, modify, publish, use, compile, sell, or -distribute this software, either in source code form or as a compiled -binary, for any purpose, commercial or non-commercial, and by any -means. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -In jurisdictions that recognize copyright laws, the author or authors -of this software dedicate any and all copyright interest in the -software to the public domain. We make this dedication for the benefit -of the public at large and to the detriment of our heirs and -successors. We intend this dedication to be an overt act of -relinquishment in perpetuity of all present and future rights to this -software under copyright law. +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR -OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. - -For more information, please refer to +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. ## Utility: Measure tool diff --git a/deps/cziplib b/deps/cziplib index 7a57414261..81314fff0a 160000 --- a/deps/cziplib +++ b/deps/cziplib @@ -1 +1 @@ -Subproject commit 7a57414261361ca991ff8053881343eb6bb6f205 +Subproject commit 81314fff0a882b72a9ad321e7a3311660125b56e From a2c9517bed69343611b89321548cd29d77790176 Mon Sep 17 00:00:00 2001 From: Muhammad Danish Date: Fri, 13 Jun 2025 03:48:05 +0500 Subject: [PATCH 065/117] Updates to WinGet publish script (#40002) Few updates to the WinGet publish workflow action - Use GitHub release event instead of manually making an API call to GitHub API and then fetching the target release. The target release is directly accessible via the event. The refactor is similar to the GitHub action of [microsoft/edit](https://github.com/microsoft/edit/blob/main/.github/workflows/winget.yml) repo's workflow - With the latest winget-create release, the preferred method for providing the GitHub token in CI/CD environment is via the environment variable `WINGET_CREATE_GITHUB_TOKEN`. Removed use of `--token` and switched to environment variable. See https://aka.ms/winget-create-token for details. --- .github/actions/spell-check/expect.txt | 1 + .github/workflows/package-submissions.yml | 39 +++++++++++++---------- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index a7012852e7..510e9ccc11 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -752,6 +752,7 @@ iwr jfif jgeosdfsdsgmkedfgdfgdfgbkmhcgcflmi jjw +JLO jobject jpe jpnime diff --git a/.github/workflows/package-submissions.yml b/.github/workflows/package-submissions.yml index b03c18b78b..a2d401faa4 100644 --- a/.github/workflows/package-submissions.yml +++ b/.github/workflows/package-submissions.yml @@ -1,5 +1,4 @@ name: WinGet submission on release -# based off of https://github.com/nushell/nushell/blob/main/.github/workflows/winget-submission.yml on: workflow_dispatch: @@ -9,23 +8,31 @@ on: jobs: winget: name: Publish winget package + + # winget-create is only supported on Windows runs-on: windows-latest + + # winget-create will read the following environment variable to access the GitHub token needed for submitting a PR + # See https://aka.ms/winget-create-token + env: + WINGET_CREATE_GITHUB_TOKEN: ${{ secrets.PT_WINGET }} + + # Only submit stable releases + if: ${{ !github.event.release.prerelease }} steps: - name: Submit Microsoft.PowerToys package to Windows Package Manager Community Repository run: | + # Get installer info from GitHub release event + $assets = '${{ toJSON(github.event.release.assets) }}' | ConvertFrom-Json + $x64UserInstallerUrl = $assets | Where-Object -Property name -match 'PowerToysUserSetup.*x64' | Select -ExpandProperty browser_download_url + $x64MachineInstallerUrl = $assets | Where-Object -Property name -match 'PowerToysSetup.*x64' | Select -ExpandProperty browser_download_url + $arm64UserInstallerUrl = $assets | Where-Object -Property name -match 'PowerToysUserSetup.*arm64' | Select -ExpandProperty browser_download_url + $arm64MachineInstallerUrl = $assets | Where-Object -Property name -match 'PowerToysSetup.*arm64' | Select -ExpandProperty browser_download_url + $packageVersion = (${{ toJSON(github.event.release.tag_name) }}).Trim('v') - $wingetPackage = "Microsoft.PowerToys" - $gitToken = "${{ secrets.PT_WINGET }}" - - $github = Invoke-RestMethod -uri "https://api.github.com/repos/Microsoft/PowerToys/releases" - - $targetRelease = $github | Where-Object -Property name -match 'Release'| Select -First 1 - $installerUserX64Url = $targetRelease | Select -ExpandProperty assets -First 1 | Where-Object -Property name -match 'PowerToysUserSetup.*x64' | Select -ExpandProperty browser_download_url - $installerMachineX64Url = $targetRelease | Select -ExpandProperty assets -First 1 | Where-Object -Property name -match 'PowerToysSetup.*x64' | Select -ExpandProperty browser_download_url - $installerUserArmUrl = $targetRelease | Select -ExpandProperty assets -First 1 | Where-Object -Property name -match 'PowerToysUserSetup.*arm64' | Select -ExpandProperty browser_download_url - $installerMachineArmUrl = $targetRelease | Select -ExpandProperty assets -First 1 | Where-Object -Property name -match 'PowerToysSetup.*arm64' | Select -ExpandProperty browser_download_url - $ver = $targetRelease.tag_name -ireplace '^v' - - # getting latest wingetcreate file - iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe - .\wingetcreate.exe update $wingetPackage -s -v $ver -u "$installerUserX64Url|user" "$installerMachineX64Url|machine" "$installerUserArmUrl|user" "$installerMachineArmUrl|machine" -t $gitToken + # Update package using wingetcreate + curl.exe -JLO https://aka.ms/wingetcreate/latest + .\wingetcreate.exe update Microsoft.PowerToys ` + --version $packageVersion ` + --urls "$x64UserInstallerUrl|user" "$x64MachineInstallerUrl|machine" "$arm64UserInstallerUrl|user" "$arm64MachineInstallerUrl|machine" ` + --submit From e5b2b7fe82950d8ef9b94ed9ae3971df39b4a70e Mon Sep 17 00:00:00 2001 From: HO-COOH Date: Fri, 13 Jun 2025 09:58:59 +0800 Subject: [PATCH 066/117] Fix fancyzone editor dpi awareness (#39356) ## Summary of the Pull Request Enable dpi aware of FancyZone Editor. Context: I happened to drag the editor window to my other monitor that has a different dpi, and the text are blurry. ## PR Checklist ## Detailed Description of the Pull Request / Additional comments ## Validation Steps Performed Manual. --- src/modules/Workspaces/WorkspacesEditor/app.manifest | 2 +- src/modules/fancyzones/editor/FancyZonesEditor/app.manifest | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/Workspaces/WorkspacesEditor/app.manifest b/src/modules/Workspaces/WorkspacesEditor/app.manifest index 598c47dd41..98afec4cae 100644 --- a/src/modules/Workspaces/WorkspacesEditor/app.manifest +++ b/src/modules/Workspaces/WorkspacesEditor/app.manifest @@ -51,7 +51,7 @@ also set the 'EnableWindowsFormsHighDpiAutoResizing' setting to 'true' in their app.config. --> - System + PerMonitorV2 diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/app.manifest b/src/modules/fancyzones/editor/FancyZonesEditor/app.manifest index 598c47dd41..98afec4cae 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/app.manifest +++ b/src/modules/fancyzones/editor/FancyZonesEditor/app.manifest @@ -51,7 +51,7 @@ also set the 'EnableWindowsFormsHighDpiAutoResizing' setting to 'true' in their app.config. --> - System + PerMonitorV2 From 38cae3ead8561cc53c2fa96685194a6f416020eb Mon Sep 17 00:00:00 2001 From: Clint Rutkas Date: Fri, 13 Jun 2025 02:00:20 +0000 Subject: [PATCH 067/117] Adjusting community.md (#40021) adding Zach and removing Zhiwei --- .github/actions/spell-check/allow/names.txt | 1 + COMMUNITY.md | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/actions/spell-check/allow/names.txt b/.github/actions/spell-check/allow/names.txt index 06b8572a26..f5d259c0f2 100644 --- a/.github/actions/spell-check/allow/names.txt +++ b/.github/actions/spell-check/allow/names.txt @@ -173,6 +173,7 @@ Tadele talynone Taras TBM +Teutsch tilovell Triet urnotdfs diff --git a/COMMUNITY.md b/COMMUNITY.md index 411aad304b..4c2ad28db0 100644 --- a/COMMUNITY.md +++ b/COMMUNITY.md @@ -21,7 +21,7 @@ Connor was the creator of Workspaces and helped create Command Palette (PowerToy ### [@damienleroy](https://github.com/damienleroy) - [Damien Leroy](https://www.linkedin.com/in/Damien-Leroy-b2734416a/) Damien has helped out by developing and contributing the Quick Accent utility. -### [@daverayment ](https://github.com/daverayment) - [David Rayment](https://www.linkedin.com/in/david-rayment-168b5251/) +### [@daverayment](https://github.com/daverayment) - [David Rayment](https://www.linkedin.com/in/david-rayment-168b5251/) Dave has helped improve the experience inside of Peek by adding in new features and fixing bugs. ### [@davidegiacometti](https://github.com/davidegiacometti) - [Davide Giacometti](https://www.linkedin.com/in/davidegiacometti/) @@ -184,7 +184,6 @@ ZoomIt source code was originally implemented by [Sysinternals](https://sysinter - [@nguyen-dows](https://github.com/nguyen-dows) - Christopher Nguyen - Product Manager - [@craigloewen-msft](https://github.com/craigloewen-msft) - Craig Loewen - Product Manager - [@niels9001](https://github.com/niels9001/) - Niels Laute - Product Manager -- [@zhiwei-ms](https://github.com/zhiwei-ms) - Zhiwei Yu - Product Manager - [@dhowett](https://github.com/dhowett) - Dustin Howett - Dev lead - [@yeelam-gordon](https://github.com/yeelam-gordon) - Gordon Lam - Dev lead - [@jamrobot](https://github.com/jamrobot) - Jerry Xu - Dev lead @@ -205,6 +204,7 @@ ZoomIt source code was originally implemented by [Sysinternals](https://sysinter - [@chatasweetie](https://github.com/chatasweetie) - Jessica Earley-Cha - Dev - [@MichaelJolley](https://github.com/MichaelJolley) - Michael Jolley - Dev - [@Jaylyn-Barbee](https://github.com/Jaylyn-Barbee) - Jaylyn Barbee - Dev +- [@zateutsch](https://github.com/zateutsch) - Zach Teutsch - Dev - [@crutkas](https://github.com/crutkas/) - Clint Rutkas - Overhead ## Former PowerToys core team members From 2b40c4d2f34a74e4a0c8ae9920ed93014c051828 Mon Sep 17 00:00:00 2001 From: Mengyuan <162882040+chenmy77@users.noreply.github.com> Date: Fri, 13 Jun 2025 10:20:04 +0800 Subject: [PATCH 068/117] [Fuzzing Tests] Use valid areaPath in OneFuzz configuration and Update jobNotificationEmail (#39994) ## Summary of the Pull Request ![image](https://github.com/user-attachments/assets/53c14889-88d3-4a77-af96-314d29a4a410) Update the areapath in the OneFuzzConfig.json to "OS\\Windows Client and Services\\WinPD\\DFX-Developer Fundamentals and Experiences\\DEFT\\SALT", ## PR Checklist - [ ] **Closes:** #xxx - [ ] **Communication:** I've discussed this with core contributors already. If the work hasn't been agreed, this work might be rejected - [ ] **Tests:** Added/updated and all pass - [ ] **Localization:** All end-user-facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx ## Detailed Description of the Pull Request / Additional comments ## Validation Steps Performed --- .../AdvancedPaste/AdvancedPaste.FuzzTests/OneFuzzConfig.json | 2 +- src/modules/Hosts/Hosts.FuzzTests/OneFuzzConfig.json | 2 +- .../fancyzones/FancyZones.FuzzTests/OneFuzzConfig.json | 4 ++-- .../RegistryPreview.FuzzTests/OneFuzzConfig.json | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/modules/AdvancedPaste/AdvancedPaste.FuzzTests/OneFuzzConfig.json b/src/modules/AdvancedPaste/AdvancedPaste.FuzzTests/OneFuzzConfig.json index bec199b9c0..ca5b104cfe 100644 --- a/src/modules/AdvancedPaste/AdvancedPaste.FuzzTests/OneFuzzConfig.json +++ b/src/modules/AdvancedPaste/AdvancedPaste.FuzzTests/OneFuzzConfig.json @@ -20,7 +20,7 @@ "AreaPath": "OS\\Windows Client and Services\\WinPD\\DFX-Developer Fundamentals and Experiences\\DEFT\\SALT", "IterationPath": "OS\\Future" }, - "jobNotificationEmail": "leilzh@microsoft.com", + "jobNotificationEmail": "PowerToys@microsoft.com", "skip": false, "rebootAfterSetup": false, "oneFuzzJobs": [ diff --git a/src/modules/Hosts/Hosts.FuzzTests/OneFuzzConfig.json b/src/modules/Hosts/Hosts.FuzzTests/OneFuzzConfig.json index 7530d3292c..b7f9b142b6 100644 --- a/src/modules/Hosts/Hosts.FuzzTests/OneFuzzConfig.json +++ b/src/modules/Hosts/Hosts.FuzzTests/OneFuzzConfig.json @@ -143,7 +143,7 @@ "AreaPath": "OS\\Windows Client and Services\\WinPD\\DFX-Developer Fundamentals and Experiences\\DEFT\\SALT", "IterationPath": "OS\\Future" }, - "jobNotificationEmail": "mengyuanchen@microsoft.com", + "jobNotificationEmail": "PowerToys@microsoft.com", "skip": false, "rebootAfterSetup": false, "oneFuzzJobs": [ diff --git a/src/modules/fancyzones/FancyZones.FuzzTests/OneFuzzConfig.json b/src/modules/fancyzones/FancyZones.FuzzTests/OneFuzzConfig.json index 01abd5594e..9bbb66816a 100644 --- a/src/modules/fancyzones/FancyZones.FuzzTests/OneFuzzConfig.json +++ b/src/modules/fancyzones/FancyZones.FuzzTests/OneFuzzConfig.json @@ -17,10 +17,10 @@ "org": "microsoft", "project": "OS", "AssignedTo": "mengyuanchen@microsoft.com", - "AreaPath": "OS\\Windows Client and Services\\WinPD\\DEEP-Developer Experience, Ecosystem and Partnerships\\SHINE\\PowerToys", + "AreaPath": "OS\\Windows Client and Services\\WinPD\\DFX-Developer Fundamentals and Experiences\\DEFT\\SALT", "IterationPath": "OS\\Future" }, - "jobNotificationEmail": "mengyuanchen@microsoft.com", + "jobNotificationEmail": "PowerToys@microsoft.com", "skip": false, "rebootAfterSetup": false, "oneFuzzJobs": [ diff --git a/src/modules/registrypreview/RegistryPreview.FuzzTests/OneFuzzConfig.json b/src/modules/registrypreview/RegistryPreview.FuzzTests/OneFuzzConfig.json index 13cff209b2..86340eedc0 100644 --- a/src/modules/registrypreview/RegistryPreview.FuzzTests/OneFuzzConfig.json +++ b/src/modules/registrypreview/RegistryPreview.FuzzTests/OneFuzzConfig.json @@ -23,7 +23,7 @@ "AreaPath": "OS\\Windows Client and Services\\WinPD\\DFX-Developer Fundamentals and Experiences\\DEFT\\SALT", "IterationPath": "OS\\Future" }, - "jobNotificationEmail": "mengyuanchen@microsoft.com", + "jobNotificationEmail": "PowerToys@microsoft.com", "skip": false, "rebootAfterSetup": false, "oneFuzzJobs": [ From 8506b47544d3c371938a6cec1d9b572152a8d41e Mon Sep 17 00:00:00 2001 From: Kai Tao <69313318+vanzue@users.noreply.github.com> Date: Fri, 13 Jun 2025 10:51:22 +0800 Subject: [PATCH 069/117] Workspaceslib ut (#40007) This pull request introduces a new unit testing framework for the `WorkspacesLib` module, adds test coverage for various utilities, and integrates the new test project into the build system. Key changes include adding the `WorkspacesLibUnitTests` project, implementing tests for `AppUtils`, `JsonUtils`, and `PwaHelper`, and updating the build configuration to include the new test project. ### Unit Tests Added: * **`AppUtilsTests`**: - Added comprehensive tests for methods such as `GetCurrentFolder`, `IsEdge`, `IsChrome`, and `IsSteamGame` to validate their behavior under various conditions. * **`JsonUtilsTests`**: - Implemented tests for reading and writing workspace data, including scenarios for invalid JSON, non-existent files, and valid workspace lists. * **`PwaHelperTests`**: - Added tests to ensure the stability of `PwaHelper` methods, such as `GetEdgeAppId`, `GetChromeAppId`, and `SearchPwaName`, even with invalid or empty inputs. ### Build System Updates: * **New Test Project Integration**: - Added the `WorkspacesLibUnitTests` project to the solution file `PowerToys.sln` with appropriate dependencies. - Updated build configurations to include the new test project for both Debug and Release builds across architectures. [[1]](diffhunk://#diff-ca837ce490070b91656ffffe31cbad8865ba9174e0f020231f77baf35ff3f811R2208-R2215) [[2]](diffhunk://#diff-ca837ce490070b91656ffffe31cbad8865ba9174e0f020231f77baf35ff3f811R2841) ### Pipeline Adjustments: * **Test Discovery**: - Updated the pipeline configuration in `.pipelines/v2/templates/job-build-project.yml` to discover the new `WorkspacesLibUnitTests.dll` during test runs. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> --- .pipelines/v2/templates/job-build-project.yml | 1 + PowerToys.sln | 30 ++- .../WorkspacesLib.UnitTests/AppUtilsTests.cpp | 239 ++++++++++++++++++ .../JsonUtilsTests.cpp | 186 ++++++++++++++ .../PwaHelperTests.cpp | 114 +++++++++ .../StringUtilsTests.cpp | 115 +++++++++ .../WorkspacesDataTests.cpp | 194 ++++++++++++++ .../WorkspacesLibUnitTests.vcxproj | 76 ++++++ .../WorkspacesLibUnitTests.vcxproj.filters | 48 ++++ .../WorkspacesLib.UnitTests/packages.config | 4 + .../WorkspacesLib.UnitTests/pch.cpp | 1 + .../Workspaces/WorkspacesLib.UnitTests/pch.h | 21 ++ .../WorkspacesLib.UnitTests/targetver.h | 8 + .../Workspaces/WorkspacesLib/StringUtils.h | 2 +- .../Workspaces/WorkspacesLib/WorkspacesData.h | 6 +- 15 files changed, 1033 insertions(+), 12 deletions(-) create mode 100644 src/modules/Workspaces/WorkspacesLib.UnitTests/AppUtilsTests.cpp create mode 100644 src/modules/Workspaces/WorkspacesLib.UnitTests/JsonUtilsTests.cpp create mode 100644 src/modules/Workspaces/WorkspacesLib.UnitTests/PwaHelperTests.cpp create mode 100644 src/modules/Workspaces/WorkspacesLib.UnitTests/StringUtilsTests.cpp create mode 100644 src/modules/Workspaces/WorkspacesLib.UnitTests/WorkspacesDataTests.cpp create mode 100644 src/modules/Workspaces/WorkspacesLib.UnitTests/WorkspacesLibUnitTests.vcxproj create mode 100644 src/modules/Workspaces/WorkspacesLib.UnitTests/WorkspacesLibUnitTests.vcxproj.filters create mode 100644 src/modules/Workspaces/WorkspacesLib.UnitTests/packages.config create mode 100644 src/modules/Workspaces/WorkspacesLib.UnitTests/pch.cpp create mode 100644 src/modules/Workspaces/WorkspacesLib.UnitTests/pch.h create mode 100644 src/modules/Workspaces/WorkspacesLib.UnitTests/targetver.h diff --git a/.pipelines/v2/templates/job-build-project.yml b/.pipelines/v2/templates/job-build-project.yml index d386ec15dd..85822a4ee8 100644 --- a/.pipelines/v2/templates/job-build-project.yml +++ b/.pipelines/v2/templates/job-build-project.yml @@ -397,6 +397,7 @@ jobs: **\UnitTests-CommonLib.dll **\PowerRenameUnitTests.dll **\UnitTests-FancyZones.dll + **\\WorkspacesLibUnitTests.dll !**\obj\** - pwsh: |- diff --git a/PowerToys.sln b/PowerToys.sln index 1db5745527..fdf0be72f8 100644 --- a/PowerToys.sln +++ b/PowerToys.sln @@ -604,6 +604,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WindowProperties", "WindowP EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WorkspacesLib", "src\modules\Workspaces\WorkspacesLib\WorkspacesLib.vcxproj", "{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WorkspacesLibUnitTests", "src\modules\Workspaces\WorkspacesLib.UnitTests\WorkspacesLibUnitTests.vcxproj", "{A85D4D9F-9A39-4B5D-8B5A-9F2D5C9A8B4C}" + ProjectSection(ProjectDependencies) = postProject + {B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332} = {B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332} + EndProjectSection +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkspacesLauncherUI", "src\modules\Workspaces\WorkspacesLauncherUI\WorkspacesLauncherUI.csproj", "{9C53CC25-0623-4569-95BC-B05410675EE3}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WorkspacesModuleInterface", "src\modules\Workspaces\WorkspacesModuleInterface\WorkspacesModuleInterface.vcxproj", "{45285DF2-9742-4ECA-9AC9-58951FC26489}" @@ -2200,6 +2205,14 @@ Global {B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Release|ARM64.Build.0 = Release|ARM64 {B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Release|x64.ActiveCfg = Release|x64 {B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Release|x64.Build.0 = Release|x64 + {A85D4D9F-9A39-4B5D-8B5A-9F2D5C9A8B4C}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {A85D4D9F-9A39-4B5D-8B5A-9F2D5C9A8B4C}.Debug|ARM64.Build.0 = Debug|ARM64 + {A85D4D9F-9A39-4B5D-8B5A-9F2D5C9A8B4C}.Debug|x64.ActiveCfg = Debug|x64 + {A85D4D9F-9A39-4B5D-8B5A-9F2D5C9A8B4C}.Debug|x64.Build.0 = Debug|x64 + {A85D4D9F-9A39-4B5D-8B5A-9F2D5C9A8B4C}.Release|ARM64.ActiveCfg = Release|ARM64 + {A85D4D9F-9A39-4B5D-8B5A-9F2D5C9A8B4C}.Release|ARM64.Build.0 = Release|ARM64 + {A85D4D9F-9A39-4B5D-8B5A-9F2D5C9A8B4C}.Release|x64.ActiveCfg = Release|x64 + {A85D4D9F-9A39-4B5D-8B5A-9F2D5C9A8B4C}.Release|x64.Build.0 = Release|x64 {9C53CC25-0623-4569-95BC-B05410675EE3}.Debug|ARM64.ActiveCfg = Debug|ARM64 {9C53CC25-0623-4569-95BC-B05410675EE3}.Debug|ARM64.Build.0 = Debug|ARM64 {9C53CC25-0623-4569-95BC-B05410675EE3}.Debug|x64.ActiveCfg = Debug|x64 @@ -2584,6 +2597,14 @@ Global {64B88F02-CD88-4ED8-9624-989A800230F9}.Release|ARM64.Build.0 = Release|ARM64 {64B88F02-CD88-4ED8-9624-989A800230F9}.Release|x64.ActiveCfg = Release|x64 {64B88F02-CD88-4ED8-9624-989A800230F9}.Release|x64.Build.0 = Release|x64 + {0217E86E-3476-9946-DE8E-9D200CEBD47A}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {0217E86E-3476-9946-DE8E-9D200CEBD47A}.Debug|ARM64.Build.0 = Debug|ARM64 + {0217E86E-3476-9946-DE8E-9D200CEBD47A}.Debug|x64.ActiveCfg = Debug|x64 + {0217E86E-3476-9946-DE8E-9D200CEBD47A}.Debug|x64.Build.0 = Debug|x64 + {0217E86E-3476-9946-DE8E-9D200CEBD47A}.Release|ARM64.ActiveCfg = Release|ARM64 + {0217E86E-3476-9946-DE8E-9D200CEBD47A}.Release|ARM64.Build.0 = Release|ARM64 + {0217E86E-3476-9946-DE8E-9D200CEBD47A}.Release|x64.ActiveCfg = Release|x64 + {0217E86E-3476-9946-DE8E-9D200CEBD47A}.Release|x64.Build.0 = Release|x64 {5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2}.Debug|ARM64.ActiveCfg = Debug|ARM64 {5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2}.Debug|ARM64.Build.0 = Debug|ARM64 {5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2}.Debug|x64.ActiveCfg = Debug|x64 @@ -2598,14 +2619,6 @@ Global {2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}.Release|ARM64.ActiveCfg = Release|ARM64 {2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}.Release|x64.ActiveCfg = Release|x64 {2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}.Release|x64.Build.0 = Release|x64 - {0217E86E-3476-9946-DE8E-9D200CEBD47A}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {0217E86E-3476-9946-DE8E-9D200CEBD47A}.Debug|ARM64.Build.0 = Debug|ARM64 - {0217E86E-3476-9946-DE8E-9D200CEBD47A}.Debug|x64.ActiveCfg = Debug|x64 - {0217E86E-3476-9946-DE8E-9D200CEBD47A}.Debug|x64.Build.0 = Debug|x64 - {0217E86E-3476-9946-DE8E-9D200CEBD47A}.Release|ARM64.ActiveCfg = Release|ARM64 - {0217E86E-3476-9946-DE8E-9D200CEBD47A}.Release|ARM64.Build.0 = Release|ARM64 - {0217E86E-3476-9946-DE8E-9D200CEBD47A}.Release|x64.ActiveCfg = Release|x64 - {0217E86E-3476-9946-DE8E-9D200CEBD47A}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2825,6 +2838,7 @@ Global {BE126CBB-AE12-406A-9837-A05ACFCA57A7} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF} {14CB58B7-D280-4A7A-95DE-4B2DF14EA000} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF} {B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF} + {A85D4D9F-9A39-4B5D-8B5A-9F2D5C9A8B4C} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF} {9C53CC25-0623-4569-95BC-B05410675EE3} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF} {45285DF2-9742-4ECA-9AC9-58951FC26489} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF} {3D63307B-9D27-44FD-B033-B26F39245B85} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF} diff --git a/src/modules/Workspaces/WorkspacesLib.UnitTests/AppUtilsTests.cpp b/src/modules/Workspaces/WorkspacesLib.UnitTests/AppUtilsTests.cpp new file mode 100644 index 0000000000..526db09d8c --- /dev/null +++ b/src/modules/Workspaces/WorkspacesLib.UnitTests/AppUtilsTests.cpp @@ -0,0 +1,239 @@ +#include "pch.h" +#include // Add this line + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +namespace WorkspacesLibUnitTests +{ + TEST_CLASS(AppUtilsTests) + { + public: + TEST_METHOD(GetCurrentFolder_ReturnsNonEmptyPath) + { + // Act + const std::wstring& result = Utils::Apps::GetCurrentFolder(); + + // Assert + Assert::IsFalse(result.empty()); + Assert::IsTrue(std::filesystem::exists(result)); + } + + TEST_METHOD(GetCurrentFolderUpper_ReturnsUppercasePath) + { + // Act + const std::wstring& currentFolder = Utils::Apps::GetCurrentFolder(); + const std::wstring& currentFolderUpper = Utils::Apps::GetCurrentFolderUpper(); + + // Assert + Assert::IsFalse(currentFolderUpper.empty()); + Assert::AreEqual(currentFolder.length(), currentFolderUpper.length()); + + // Verify it's actually uppercase + std::wstring expectedUpper = currentFolder; + std::transform(expectedUpper.begin(), expectedUpper.end(), expectedUpper.begin(), towupper); + Assert::AreEqual(expectedUpper, currentFolderUpper); + } + + TEST_METHOD(GetCurrentFolder_ConsistentResults) + { + // Act + const std::wstring& result1 = Utils::Apps::GetCurrentFolder(); + const std::wstring& result2 = Utils::Apps::GetCurrentFolder(); + + // Assert + Assert::AreEqual(result1, result2); + } + + TEST_METHOD(GetCurrentFolderUpper_ConsistentResults) + { + // Act + const std::wstring& result1 = Utils::Apps::GetCurrentFolderUpper(); + const std::wstring& result2 = Utils::Apps::GetCurrentFolderUpper(); + + // Assert + Assert::AreEqual(result1, result2); + } + + TEST_METHOD(AppData_IsEdge_EdgePath_ReturnsTrue) + { + // Arrange + Utils::Apps::AppData appData; + appData.installPath = L"C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe"; + + // Act + bool result = appData.IsEdge(); + + // Assert + Assert::IsTrue(result); + } + + TEST_METHOD(AppData_IsEdge_NonEdgePath_ReturnsFalse) + { + // Arrange + Utils::Apps::AppData appData; + appData.installPath = L"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"; + + // Act + bool result = appData.IsEdge(); + + // Assert + Assert::IsFalse(result); + } + + TEST_METHOD(AppData_IsEdge_EmptyPath_ReturnsFalse) + { + // Arrange + Utils::Apps::AppData appData; + appData.installPath = L""; + + // Act + bool result = appData.IsEdge(); + + // Assert + Assert::IsFalse(result); + } + + TEST_METHOD(AppData_IsChrome_ChromePath_ReturnsTrue) + { + // Arrange + Utils::Apps::AppData appData; + appData.installPath = L"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"; + + // Act + bool result = appData.IsChrome(); + + // Assert + Assert::IsTrue(result); + } + + TEST_METHOD(AppData_IsChrome_NonChromePath_ReturnsFalse) + { + // Arrange + Utils::Apps::AppData appData; + appData.installPath = L"C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe"; + + // Act + bool result = appData.IsChrome(); + + // Assert + Assert::IsFalse(result); + } + + TEST_METHOD(AppData_IsChrome_EmptyPath_ReturnsFalse) + { + // Arrange + Utils::Apps::AppData appData; + appData.installPath = L""; + + // Act + bool result = appData.IsChrome(); + + // Assert + Assert::IsFalse(result); + } + + TEST_METHOD(AppData_IsSteamGame_SteamProtocol_ReturnsTrue) + { + // Arrange + Utils::Apps::AppData appData; + appData.protocolPath = L"steam://run/123456"; + + // Act + bool result = appData.IsSteamGame(); + + // Assert + Assert::IsTrue(result); + } + + TEST_METHOD(AppData_IsSteamGame_NonSteamProtocol_ReturnsFalse) + { + // Arrange + Utils::Apps::AppData appData; + appData.protocolPath = L"https://example.com"; + + // Act + bool result = appData.IsSteamGame(); + + // Assert + Assert::IsFalse(result); + } + + TEST_METHOD(AppData_IsSteamGame_EmptyProtocol_ReturnsFalse) + { + // Arrange + Utils::Apps::AppData appData; + appData.protocolPath = L""; + + // Act + bool result = appData.IsSteamGame(); + + // Assert + Assert::IsFalse(result); + } + + TEST_METHOD(AppData_IsSteamGame_PartialSteamString_ReturnsFalse) + { + // Arrange + Utils::Apps::AppData appData; + appData.protocolPath = L"http://run/123456"; + + // Act + bool result = appData.IsSteamGame(); + + // Assert + Assert::IsFalse(result); + } + + TEST_METHOD(AppData_DefaultValues) + { + // Arrange & Act + Utils::Apps::AppData appData; + + // Assert + Assert::IsTrue(appData.name.empty()); + Assert::IsTrue(appData.installPath.empty()); + Assert::IsTrue(appData.packageFullName.empty()); + Assert::IsTrue(appData.appUserModelId.empty()); + Assert::IsTrue(appData.pwaAppId.empty()); + Assert::IsTrue(appData.protocolPath.empty()); + Assert::IsFalse(appData.canLaunchElevated); + } + + TEST_METHOD(AppData_MultipleBrowserDetection) + { + // Arrange + Utils::Apps::AppData edgeApp; + edgeApp.installPath = L"C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe"; + + Utils::Apps::AppData chromeApp; + chromeApp.installPath = L"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"; + + Utils::Apps::AppData otherApp; + otherApp.installPath = L"C:\\Program Files\\Firefox\\firefox.exe"; + + // Act & Assert + Assert::IsTrue(edgeApp.IsEdge()); + Assert::IsFalse(edgeApp.IsChrome()); + Assert::IsFalse(edgeApp.IsSteamGame()); + + Assert::IsFalse(chromeApp.IsEdge()); + Assert::IsTrue(chromeApp.IsChrome()); + Assert::IsFalse(chromeApp.IsSteamGame()); + + Assert::IsFalse(otherApp.IsEdge()); + Assert::IsFalse(otherApp.IsChrome()); + Assert::IsFalse(otherApp.IsSteamGame()); + } + + TEST_METHOD(GetAppsList_ReturnsAppList) + { + // Act + Utils::Apps::AppList apps = Utils::Apps::GetAppsList(); + + // Assert + // The list can be empty or non-empty depending on the system + // But it should not crash and should return a valid list + Assert::IsTrue(apps.size() >= 0); + } + }; +} \ No newline at end of file diff --git a/src/modules/Workspaces/WorkspacesLib.UnitTests/JsonUtilsTests.cpp b/src/modules/Workspaces/WorkspacesLib.UnitTests/JsonUtilsTests.cpp new file mode 100644 index 0000000000..863efb82bb --- /dev/null +++ b/src/modules/Workspaces/WorkspacesLib.UnitTests/JsonUtilsTests.cpp @@ -0,0 +1,186 @@ +#include "pch.h" +#include +#include + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +namespace WorkspacesLibUnitTests +{ + TEST_CLASS (JsonUtilsTests) + { + private: + std::wstring CreateTempJsonFile(const std::wstring& content) + { + std::wstring tempPath = std::filesystem::temp_directory_path(); + tempPath += L"\\test_workspace_" + std::to_wstring(GetTickCount64()) + L".json"; + + std::wofstream file(tempPath); + file << content; + file.close(); + + return tempPath; + } + + void DeleteTempFile(const std::wstring& filePath) + { + if (std::filesystem::exists(filePath)) + { + std::filesystem::remove(filePath); + } + } + + public: + TEST_METHOD (ReadSingleWorkspace_NonExistentFile_ReturnsEmptyWorkspace) + { + // Arrange + std::wstring nonExistentFile = L"C:\\NonExistent\\File.json"; + + // Act + auto result = JsonUtils::ReadSingleWorkspace(nonExistentFile); + + // Assert + Assert::IsTrue(result.isOk()); + auto workspace = result.value(); + Assert::IsTrue(workspace.name.empty()); + } + + TEST_METHOD (ReadSingleWorkspace_InvalidJsonFile_ReturnsError) + { + // Arrange + std::wstring tempFile = CreateTempJsonFile(L"invalid json content {"); + + // Act + auto result = JsonUtils::ReadSingleWorkspace(tempFile); + + // Assert + Assert::IsTrue(result.isError()); + Assert::AreEqual(static_cast(JsonUtils::WorkspacesFileError::IncorrectFileError), + static_cast(result.error())); + + // Cleanup + DeleteTempFile(tempFile); + } + + TEST_METHOD (ReadWorkspaces_NonExistentFile_ReturnsEmptyVector) + { + // Arrange + std::wstring nonExistentFile = L"C:\\NonExistent\\File.json"; + + // Act + auto result = JsonUtils::ReadWorkspaces(nonExistentFile); + + // Assert + Assert::IsTrue(result.isError()); + Assert::AreEqual(static_cast(JsonUtils::WorkspacesFileError::IncorrectFileError), + static_cast(result.error())); + } + + TEST_METHOD (ReadWorkspaces_InvalidJsonFile_ReturnsError) + { + // Arrange + std::wstring tempFile = CreateTempJsonFile(L"invalid json content {"); + + // Act + auto result = JsonUtils::ReadWorkspaces(tempFile); + + // Assert + Assert::IsTrue(result.isError()); + Assert::AreEqual(static_cast(JsonUtils::WorkspacesFileError::IncorrectFileError), + static_cast(result.error())); + + // Cleanup + DeleteTempFile(tempFile); + } + + TEST_METHOD (Write_ValidWorkspace_ReturnsTrue) + { + // Arrange + std::wstring tempPath = std::filesystem::temp_directory_path(); + tempPath += L"\\test_write_workspace_" + std::to_wstring(GetTickCount64()) + L".json"; + + WorkspacesData::WorkspacesProject workspace; + workspace.name = L"Test Workspace"; + + // Convert string to time_t + std::tm tm = {}; + workspace.creationTime = std::mktime(&tm); + + // Act + bool result = JsonUtils::Write(tempPath, workspace); + + // Assert + Assert::IsTrue(result); + Assert::IsTrue(std::filesystem::exists(tempPath)); + + // Cleanup + DeleteTempFile(tempPath); + } + + TEST_METHOD (Write_ValidWorkspacesList_ReturnsTrue) + { + // Arrange + std::wstring tempPath = std::filesystem::temp_directory_path(); + tempPath += L"\\test_write_workspaces_" + std::to_wstring(GetTickCount64()) + L".json"; + + std::vector workspaces; + + WorkspacesData::WorkspacesProject workspace1; + workspace1.name = L"Test Workspace 1"; + workspace1.creationTime = std::time(nullptr); + + WorkspacesData::WorkspacesProject workspace2; + workspace2.name = L"Test Workspace 2"; + workspace2.creationTime = std::time(nullptr); + + workspaces.push_back(workspace1); + workspaces.push_back(workspace2); + + // Act + bool result = JsonUtils::Write(tempPath, workspaces); + + // Assert + Assert::IsTrue(result); + Assert::IsTrue(std::filesystem::exists(tempPath)); + + // Cleanup + DeleteTempFile(tempPath); + } + + TEST_METHOD (Write_EmptyWorkspacesList_ReturnsTrue) + { + // Arrange + std::wstring tempPath = std::filesystem::temp_directory_path(); + tempPath += L"\\test_write_empty_" + std::to_wstring(GetTickCount64()) + L".json"; + + std::vector emptyWorkspaces; + + // Act + bool result = JsonUtils::Write(tempPath, emptyWorkspaces); + + // Assert + Assert::IsTrue(result); + Assert::IsTrue(std::filesystem::exists(tempPath)); + + // Cleanup + DeleteTempFile(tempPath); + } + + /* + TEST_METHOD(Write_InvalidPath_ReturnsFalse) + { + // Arrange + std::wstring invalidPath = L"C:\\NonExistent\\Path\\workspace.json"; + + WorkspacesData::WorkspacesProject workspace; + workspace.name = L"Test Workspace"; + workspace.creationTime = std::time(nullptr); + + // Act + bool result = JsonUtils::Write(invalidPath, workspace); + + // Assert + Assert::IsFalse(result); + } + */ + }; +} \ No newline at end of file diff --git a/src/modules/Workspaces/WorkspacesLib.UnitTests/PwaHelperTests.cpp b/src/modules/Workspaces/WorkspacesLib.UnitTests/PwaHelperTests.cpp new file mode 100644 index 0000000000..cb3edf2c00 --- /dev/null +++ b/src/modules/Workspaces/WorkspacesLib.UnitTests/PwaHelperTests.cpp @@ -0,0 +1,114 @@ +#include "pch.h" + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +namespace WorkspacesLibUnitTests +{ + TEST_CLASS (PwaHelperTests) + { + public: + TEST_METHOD (PwaHelper_Constructor_DoesNotThrow) + { + // Act & Assert - Constructor should not crash when called + try + { + Utils::PwaHelper helper; + // If we get here, the constructor didn't throw + Assert::IsTrue(true); + } + catch (...) + { + Assert::Fail(L"PwaHelper constructor should not throw exceptions"); + } + } + + TEST_METHOD (PwaHelper_GetEdgeAppId_EmptyAumid_ReturnsEmpty) + { + // Arrange + Utils::PwaHelper helper; + std::wstring emptyAumid = L""; + + // Act + auto result = helper.GetEdgeAppId(emptyAumid); + + // Assert + Assert::IsFalse(result.has_value()); + } + + TEST_METHOD (PwaHelper_GetChromeAppId_EmptyAumid_ReturnsEmpty) + { + // Arrange + Utils::PwaHelper helper; + std::wstring emptyAumid = L""; + + // Act + auto result = helper.GetChromeAppId(emptyAumid); + + // Assert + Assert::IsFalse(result.has_value()); + } + + TEST_METHOD (PwaHelper_SearchPwaName_EmptyParameters_ReturnsEmpty) + { + // Arrange + Utils::PwaHelper helper; + std::wstring emptyPwaAppId = L""; + std::wstring emptyWindowAumid = L""; + + // Act + std::wstring result = helper.SearchPwaName(emptyPwaAppId, emptyWindowAumid); + + // Assert + Assert::IsTrue(result.empty()); + } + + TEST_METHOD (PwaHelper_SearchPwaName_NonExistentIds_ReturnsEmpty) + { + // Arrange + Utils::PwaHelper helper; + std::wstring nonExistentPwaAppId = L"nonexistent_app_id"; + std::wstring nonExistentWindowAumid = L"nonexistent_aumid"; + + // Act + std::wstring result = helper.SearchPwaName(nonExistentPwaAppId, nonExistentWindowAumid); + + // TODO: is it really expected? + Assert::IsTrue(result == nonExistentWindowAumid); + } + + TEST_METHOD (PwaHelper_GetAUMIDFromWindow_InvalidWindow_ReturnsEmpty) + { + // Arrange + Utils::PwaHelper helper; + HWND invalidWindow = nullptr; + + // Act + std::wstring result = helper.GetAUMIDFromWindow(invalidWindow); + + // Assert + Assert::IsTrue(result.empty()); + } + + TEST_METHOD (PwaHelper_GetEdgeAppId_ValidConstruction_DoesNotCrash) + { + // Arrange + Utils::PwaHelper helper; + std::wstring testAumid = L"Microsoft.MicrosoftEdge_8wekyb3d8bbwe!App"; + + // Act & Assert - Should not crash + auto result = helper.GetEdgeAppId(testAumid); + // Result can be empty or have value, but should not crash + } + + TEST_METHOD (PwaHelper_GetChromeAppId_ValidConstruction_DoesNotCrash) + { + // Arrange + Utils::PwaHelper helper; + std::wstring testAumid = L"Chrome.App.TestId"; + + // Act & Assert - Should not crash + auto result = helper.GetChromeAppId(testAumid); + // Result can be empty or have value, but should not crash + } + }; +} \ No newline at end of file diff --git a/src/modules/Workspaces/WorkspacesLib.UnitTests/StringUtilsTests.cpp b/src/modules/Workspaces/WorkspacesLib.UnitTests/StringUtilsTests.cpp new file mode 100644 index 0000000000..846ba71483 --- /dev/null +++ b/src/modules/Workspaces/WorkspacesLib.UnitTests/StringUtilsTests.cpp @@ -0,0 +1,115 @@ +#include "pch.h" +#include + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +namespace WorkspacesLibUnitTests +{ + TEST_CLASS(StringUtilsTests) + { + public: + TEST_METHOD(CaseInsensitiveEquals_SameStrings_ReturnsTrue) + { + // Arrange + std::wstring str1 = L"test"; + std::wstring str2 = L"test"; + + // Act + bool result = StringUtils::CaseInsensitiveEquals(str1, str2); + + // Assert + Assert::IsTrue(result); + } + + TEST_METHOD(CaseInsensitiveEquals_DifferentCase_ReturnsTrue) + { + // Arrange + std::wstring str1 = L"Test"; + std::wstring str2 = L"TEST"; + + // Act + bool result = StringUtils::CaseInsensitiveEquals(str1, str2); + + // Assert + Assert::IsTrue(result); + } + + TEST_METHOD(CaseInsensitiveEquals_MixedCase_ReturnsTrue) + { + // Arrange + std::wstring str1 = L"TeSt StRiNg"; + std::wstring str2 = L"test STRING"; + + // Act + bool result = StringUtils::CaseInsensitiveEquals(str1, str2); + + // Assert + Assert::IsTrue(result); + } + + TEST_METHOD(CaseInsensitiveEquals_DifferentStrings_ReturnsFalse) + { + // Arrange + std::wstring str1 = L"test"; + std::wstring str2 = L"different"; + + // Act + bool result = StringUtils::CaseInsensitiveEquals(str1, str2); + + // Assert + Assert::IsFalse(result); + } + + TEST_METHOD(CaseInsensitiveEquals_DifferentLengths_ReturnsFalse) + { + // Arrange + std::wstring str1 = L"test"; + std::wstring str2 = L"testing"; + + // Act + bool result = StringUtils::CaseInsensitiveEquals(str1, str2); + + // Assert + Assert::IsFalse(result); + } + + TEST_METHOD(CaseInsensitiveEquals_EmptyStrings_ReturnsTrue) + { + // Arrange + std::wstring str1 = L""; + std::wstring str2 = L""; + + // Act + bool result = StringUtils::CaseInsensitiveEquals(str1, str2); + + // Assert + Assert::IsTrue(result); + } + + TEST_METHOD(CaseInsensitiveEquals_OneEmpty_ReturnsFalse) + { + // Arrange + std::wstring str1 = L"test"; + std::wstring str2 = L""; + + // Act + bool result = StringUtils::CaseInsensitiveEquals(str1, str2); + + // Assert + Assert::IsFalse(result); + } + + TEST_METHOD(CaseInsensitiveEquals_SpecialCharacters_ReturnsTrue) + { + // Arrange + std::wstring str1 = L"Test-123_Special!"; + std::wstring str2 = L"test-123_special!"; + + // Act + bool result = StringUtils::CaseInsensitiveEquals(str1, str2); + + // Assert + Assert::IsTrue(result); + } + }; +} \ No newline at end of file diff --git a/src/modules/Workspaces/WorkspacesLib.UnitTests/WorkspacesDataTests.cpp b/src/modules/Workspaces/WorkspacesLib.UnitTests/WorkspacesDataTests.cpp new file mode 100644 index 0000000000..9228dbba47 --- /dev/null +++ b/src/modules/Workspaces/WorkspacesLib.UnitTests/WorkspacesDataTests.cpp @@ -0,0 +1,194 @@ +#include "pch.h" + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +namespace WorkspacesLibUnitTests +{ + TEST_CLASS(WorkspacesDataTests) + { + public: + TEST_METHOD(WorkspacesFile_ReturnsValidPath) + { + // Act + std::wstring result = WorkspacesData::WorkspacesFile(); + + // Assert + Assert::IsFalse(result.empty()); + Assert::IsTrue(result.find(L"workspaces.json") != std::wstring::npos); + } + + TEST_METHOD(TempWorkspacesFile_ReturnsValidPath) + { + // Act + std::wstring result = WorkspacesData::TempWorkspacesFile(); + + // Assert + Assert::IsFalse(result.empty()); + Assert::IsTrue(result.find(L"temp-workspaces.json") != std::wstring::npos); + } + + TEST_METHOD(WorkspacesFile_TempWorkspacesFile_DifferentPaths) + { + // Act + std::wstring workspacesFile = WorkspacesData::WorkspacesFile(); + std::wstring tempWorkspacesFile = WorkspacesData::TempWorkspacesFile(); + + // Assert + Assert::AreNotEqual(workspacesFile, tempWorkspacesFile); + } + + TEST_METHOD(Position_ToRect_ConvertsCorrectly) + { + // Arrange + WorkspacesData::WorkspacesProject::Application::Position position; + position.x = 100; + position.y = 200; + position.width = 800; + position.height = 600; + + // Act + RECT rect = position.toRect(); + + // Assert + Assert::AreEqual(100, static_cast(rect.left)); + Assert::AreEqual(200, static_cast(rect.top)); + Assert::AreEqual(900, static_cast(rect.right)); // x + width + Assert::AreEqual(800, static_cast(rect.bottom)); // y + height + } + + TEST_METHOD(Position_ToRect_ZeroPosition) + { + // Arrange + WorkspacesData::WorkspacesProject::Application::Position position; + position.x = 0; + position.y = 0; + position.width = 0; + position.height = 0; + + // Act + RECT rect = position.toRect(); + + // Assert + Assert::AreEqual(0, static_cast(rect.left)); + Assert::AreEqual(0, static_cast(rect.top)); + Assert::AreEqual(0, static_cast(rect.right)); + Assert::AreEqual(0, static_cast(rect.bottom)); + } + + TEST_METHOD(Position_ToRect_NegativeCoordinates) + { + // Arrange + WorkspacesData::WorkspacesProject::Application::Position position; + position.x = -100; + position.y = -50; + position.width = 200; + position.height = 150; + + // Act + RECT rect = position.toRect(); + + // Assert + Assert::AreEqual(-100, static_cast(rect.left)); + Assert::AreEqual(-50, static_cast(rect.top)); + Assert::AreEqual(100, static_cast(rect.right)); // -100 + 200 + Assert::AreEqual(100, static_cast(rect.bottom)); // -50 + 150 + } + + TEST_METHOD(Application_DefaultValues) + { + // Arrange & Act + WorkspacesData::WorkspacesProject::Application app; + + // Assert + Assert::IsTrue(app.id.empty()); + Assert::IsTrue(app.name.empty()); + Assert::IsTrue(app.title.empty()); + Assert::IsTrue(app.path.empty()); + Assert::IsTrue(app.packageFullName.empty()); + Assert::IsTrue(app.appUserModelId.empty()); + Assert::IsTrue(app.pwaAppId.empty()); + Assert::IsTrue(app.commandLineArgs.empty()); + Assert::IsFalse(app.isElevated); + Assert::IsFalse(app.canLaunchElevated); + Assert::IsFalse(app.isMinimized); + Assert::IsFalse(app.isMaximized); + Assert::AreEqual(0, static_cast(app.position.x)); + Assert::AreEqual(0, static_cast(app.position.y)); + Assert::AreEqual(0, static_cast(app.position.width)); + Assert::AreEqual(0, static_cast(app.position.height)); + Assert::AreEqual(0u, static_cast(app.monitor)); + } + + TEST_METHOD(Application_Comparison_EqualObjects) + { + // Arrange + WorkspacesData::WorkspacesProject::Application app1; + app1.id = L"test-id"; + app1.name = L"Test App"; + app1.position.x = 100; + app1.position.y = 200; + + WorkspacesData::WorkspacesProject::Application app2; + app2.id = L"test-id"; + app2.name = L"Test App"; + app2.position.x = 100; + app2.position.y = 200; + + // Act & Assert + Assert::IsTrue(app1 == app2); + } + + TEST_METHOD(Application_Comparison_DifferentObjects) + { + // Arrange + WorkspacesData::WorkspacesProject::Application app1; + app1.id = L"test-id-1"; + app1.name = L"Test App 1"; + + WorkspacesData::WorkspacesProject::Application app2; + app2.id = L"test-id-2"; + app2.name = L"Test App 2"; + + // Act & Assert + Assert::IsTrue(app1 != app2); + } + + TEST_METHOD(Position_Comparison_EqualPositions) + { + // Arrange + WorkspacesData::WorkspacesProject::Application::Position pos1; + pos1.x = 100; + pos1.y = 200; + pos1.width = 800; + pos1.height = 600; + + WorkspacesData::WorkspacesProject::Application::Position pos2; + pos2.x = 100; + pos2.y = 200; + pos2.width = 800; + pos2.height = 600; + + // Act & Assert + Assert::IsTrue(pos1 == pos2); + } + + TEST_METHOD(Position_Comparison_DifferentPositions) + { + // Arrange + WorkspacesData::WorkspacesProject::Application::Position pos1; + pos1.x = 100; + pos1.y = 200; + pos1.width = 800; + pos1.height = 600; + + WorkspacesData::WorkspacesProject::Application::Position pos2; + pos2.x = 150; + pos2.y = 200; + pos2.width = 800; + pos2.height = 600; + + // Act & Assert + Assert::IsTrue(pos1 != pos2); + } + }; +} \ No newline at end of file diff --git a/src/modules/Workspaces/WorkspacesLib.UnitTests/WorkspacesLibUnitTests.vcxproj b/src/modules/Workspaces/WorkspacesLib.UnitTests/WorkspacesLibUnitTests.vcxproj new file mode 100644 index 0000000000..e3d18f54f3 --- /dev/null +++ b/src/modules/Workspaces/WorkspacesLib.UnitTests/WorkspacesLibUnitTests.vcxproj @@ -0,0 +1,76 @@ + + + + + {A85D4D9F-9A39-4B5D-8B5A-9F2D5C9A8B4C} + Win32Proj + WorkspacesLibUnitTests + WorkspacesLibUnitTests + + + + v143 + + + DynamicLibrary + + + + + + + + + + + + ..\..\..\..\$(Platform)\$(Configuration)\tests\Workspaces\ + + + + ..\;..\WorkspacesLib\;$(SolutionDir)src\;$(SolutionDir)src\common;$(SolutionDir)src\common\Telemetry;..\..\;..\..\..\;$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) + WIN32;%(PreprocessorDefinitions) + true + + + $(VCInstallDir)UnitTest\\lib;$(SolutionDir)$(Platform)\\$(Configuration)\\;%(AdditionalLibraryDirectories) + propsys.lib;comctl32.lib;pathcch.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;Pathcch.lib;%(AdditionalDependencies) + + + + + + + + + Create + + + + + + + + + + + + + {6955446d-23f7-4023-9bb3-8657f904af99} + + + {b31fcc55-b5a4-4ea7-b414-2dceae6af332} + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/src/modules/Workspaces/WorkspacesLib.UnitTests/WorkspacesLibUnitTests.vcxproj.filters b/src/modules/Workspaces/WorkspacesLib.UnitTests/WorkspacesLibUnitTests.vcxproj.filters new file mode 100644 index 0000000000..71be38ab78 --- /dev/null +++ b/src/modules/Workspaces/WorkspacesLib.UnitTests/WorkspacesLibUnitTests.vcxproj.filters @@ -0,0 +1,48 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + + \ No newline at end of file diff --git a/src/modules/Workspaces/WorkspacesLib.UnitTests/packages.config b/src/modules/Workspaces/WorkspacesLib.UnitTests/packages.config new file mode 100644 index 0000000000..2c5d71ae86 --- /dev/null +++ b/src/modules/Workspaces/WorkspacesLib.UnitTests/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/modules/Workspaces/WorkspacesLib.UnitTests/pch.cpp b/src/modules/Workspaces/WorkspacesLib.UnitTests/pch.cpp new file mode 100644 index 0000000000..17305716aa --- /dev/null +++ b/src/modules/Workspaces/WorkspacesLib.UnitTests/pch.cpp @@ -0,0 +1 @@ +#include "pch.h" \ No newline at end of file diff --git a/src/modules/Workspaces/WorkspacesLib.UnitTests/pch.h b/src/modules/Workspaces/WorkspacesLib.UnitTests/pch.h new file mode 100644 index 0000000000..3a6b3adb6b --- /dev/null +++ b/src/modules/Workspaces/WorkspacesLib.UnitTests/pch.h @@ -0,0 +1,21 @@ +#pragma once + +#include "targetver.h" + +// Headers for CppUnitTest +#pragma warning(disable : 26466) +#include "CppUnitTest.h" + +// Windows headers +#include +#include +#include +#include +#include + +// Workspaces headers +#include +#include +#include +#include +#include \ No newline at end of file diff --git a/src/modules/Workspaces/WorkspacesLib.UnitTests/targetver.h b/src/modules/Workspaces/WorkspacesLib.UnitTests/targetver.h new file mode 100644 index 0000000000..5b1f29cad0 --- /dev/null +++ b/src/modules/Workspaces/WorkspacesLib.UnitTests/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include \ No newline at end of file diff --git a/src/modules/Workspaces/WorkspacesLib/StringUtils.h b/src/modules/Workspaces/WorkspacesLib/StringUtils.h index ea53ed3f5b..929ae2c26a 100644 --- a/src/modules/Workspaces/WorkspacesLib/StringUtils.h +++ b/src/modules/Workspaces/WorkspacesLib/StringUtils.h @@ -6,7 +6,7 @@ namespace StringUtils { - bool CaseInsensitiveEquals(const std::wstring& str1, const std::wstring& str2) + inline bool CaseInsensitiveEquals(const std::wstring& str1, const std::wstring& str2) { if (str1.size() != str2.size()) { diff --git a/src/modules/Workspaces/WorkspacesLib/WorkspacesData.h b/src/modules/Workspaces/WorkspacesLib/WorkspacesData.h index 272cf65d5a..5d04c527f6 100644 --- a/src/modules/Workspaces/WorkspacesLib/WorkspacesData.h +++ b/src/modules/Workspaces/WorkspacesLib/WorkspacesData.h @@ -69,10 +69,10 @@ namespace WorkspacesData std::wstring id; std::wstring name; - time_t creationTime; + time_t creationTime{}; std::optional lastLaunchedTime; - bool isShortcutNeeded; - bool moveExistingWindows; + bool isShortcutNeeded{}; + bool moveExistingWindows{}; std::vector monitors; std::vector apps; }; From 2d1676b7dfedbdf0d8bafb0a8a3db9d617e3f5e5 Mon Sep 17 00:00:00 2001 From: leileizhang Date: Fri, 13 Jun 2025 12:27:53 +0800 Subject: [PATCH 070/117] [CmdPal][AOT] Using ExprTk to make the Cal extension AOT-compatible (#39972) ## Summary of the Pull Request This PR replaces the original Mages-based expression evaluation engine with a WinRT-wrapped version of Exprtk in the CmdPalCalculator module. ### Key Changes **Expression Engine:** - All expression parsing and evaluation now use Exprtk (via a WinRT wrapper) instead of Mages. **Base Conversion Handling:** - Since Exprtk does not support non-decimal (binary, octal, hexadecimal) input natively, added logic to convert all such inputs to decimal before evaluation. **Code Structure:** - The overall logic and API surface remain unchanged except for the engine and base conversion handling. - All previous Mages references and dependencies have been removed. ## PR Checklist - [ ] **Closes:** #xxx - [ ] **Communication:** I've discussed this with core contributors already. If work hasn't been agreed, this work might be rejected - [ ] **Tests:** Added/updated and all pass - [ ] **Localization:** All end user facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx ## Validation Steps Performed --- .github/actions/spell-check/expect.txt | 3 + .pipelines/ESRPSigning_core.json | 385 +- NOTICE.md | 37 + PowerToys.sln | 11 + .../CalculatorEngineCommon/Calculator.cpp | 24 + .../CalculatorEngineCommon/Calculator.h | 25 + .../CalculatorEngineCommon/Calculator.idl | 10 + .../CalculatorEngineCommon.def | 3 + .../CalculatorEngineCommon.vcxproj | 181 + .../CalculatorEngineCommon.vcxproj.filters | 29 + .../ExprtkEvaluator.cpp | 50 + .../CalculatorEngineCommon/ExprtkEvaluator.h | 10 + .../PropertySheet.props | 16 + src/common/CalculatorEngineCommon/exprtk.hpp | 46251 ++++++++++++++++ .../CalculatorEngineCommon/packages.config | 4 + src/common/CalculatorEngineCommon/pch.cpp | 1 + src/common/CalculatorEngineCommon/pch.h | 4 + src/common/CalculatorEngineCommon/readme.md | 29 + .../Helper/CalculateEngine.cs | 55 +- .../Helper/NumberTranslator.cs | 74 +- .../Helper/QueryHelper.cs | 5 - .../Microsoft.CmdPal.Ext.Calc.csproj | 16 +- 22 files changed, 46955 insertions(+), 268 deletions(-) create mode 100644 src/common/CalculatorEngineCommon/Calculator.cpp create mode 100644 src/common/CalculatorEngineCommon/Calculator.h create mode 100644 src/common/CalculatorEngineCommon/Calculator.idl create mode 100644 src/common/CalculatorEngineCommon/CalculatorEngineCommon.def create mode 100644 src/common/CalculatorEngineCommon/CalculatorEngineCommon.vcxproj create mode 100644 src/common/CalculatorEngineCommon/CalculatorEngineCommon.vcxproj.filters create mode 100644 src/common/CalculatorEngineCommon/ExprtkEvaluator.cpp create mode 100644 src/common/CalculatorEngineCommon/ExprtkEvaluator.h create mode 100644 src/common/CalculatorEngineCommon/PropertySheet.props create mode 100644 src/common/CalculatorEngineCommon/exprtk.hpp create mode 100644 src/common/CalculatorEngineCommon/packages.config create mode 100644 src/common/CalculatorEngineCommon/pch.cpp create mode 100644 src/common/CalculatorEngineCommon/pch.h create mode 100644 src/common/CalculatorEngineCommon/readme.md diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 510e9ccc11..6e3f041fba 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -69,6 +69,7 @@ appwiz APSTUDIO AQS ARandom +Arash ARCHITEW ARemapped ARPINSTALLLOCATION @@ -469,6 +470,7 @@ EXPCMDFLAGS EXPCMDSTATE explr exppowertoys +exprtk exptas exsb exstyle @@ -1150,6 +1152,7 @@ PARTIALCONFIRMATIONDIALOGTITLE PATCOPY PATHMUSTEXIST PATINVERT +partow PATPAINT pbc pbi diff --git a/.pipelines/ESRPSigning_core.json b/.pipelines/ESRPSigning_core.json index 615b5633bf..b0b2a42655 100644 --- a/.pipelines/ESRPSigning_core.json +++ b/.pipelines/ESRPSigning_core.json @@ -3,226 +3,227 @@ "UseMinimatch": false, "SignBatches": [ { - "MatchedPath": [ - "*.resources.dll", - - "WinUI3Apps\\Assets\\Settings\\Scripts\\*.ps1", + "MatchedPath": [ + "*.resources.dll", - "PowerToys.ActionRunner.exe", - "PowerToys.Update.exe", - "PowerToys.BackgroundActivatorDLL.dll", - "Notifications.dll", - "os-detection.dll", - "PowerToys.exe", - "PowerToys.FilePreviewCommon.dll", - "PowerToys.Interop.dll", - "Tools\\PowerToys.BugReportTool.exe", - "StylesReportTool\\PowerToys.StylesReportTool.exe", - "Telemetry.dll", - "PowerToys.ManagedTelemetry.dll", - "PowerToys.ManagedCommon.dll", - "PowerToys.Common.UI.dll", - "PowerToys.Settings.UI.Lib.dll", - "PowerToys.GPOWrapper.dll", - "PowerToys.GPOWrapperProjection.dll", - "PowerToys.AllExperiments.dll", + "WinUI3Apps\\Assets\\Settings\\Scripts\\*.ps1", - "PowerToys.AlwaysOnTop.exe", - "PowerToys.AlwaysOnTopModuleInterface.dll", + "PowerToys.ActionRunner.exe", + "PowerToys.Update.exe", + "PowerToys.BackgroundActivatorDLL.dll", + "Notifications.dll", + "os-detection.dll", + "PowerToys.exe", + "PowerToys.FilePreviewCommon.dll", + "PowerToys.Interop.dll", + "Tools\\PowerToys.BugReportTool.exe", + "StylesReportTool\\PowerToys.StylesReportTool.exe", + "Telemetry.dll", + "CalculatorEngineCommon.dll", + "PowerToys.ManagedTelemetry.dll", + "PowerToys.ManagedCommon.dll", + "PowerToys.Common.UI.dll", + "PowerToys.Settings.UI.Lib.dll", + "PowerToys.GPOWrapper.dll", + "PowerToys.GPOWrapperProjection.dll", + "PowerToys.AllExperiments.dll", - "PowerToys.CmdNotFoundModuleInterface.dll", - "PowerToys.CmdNotFound.dll", + "PowerToys.AlwaysOnTop.exe", + "PowerToys.AlwaysOnTopModuleInterface.dll", - "PowerToys.ColorPicker.dll", - "PowerToys.ColorPickerUI.dll", - "PowerToys.ColorPickerUI.exe", + "PowerToys.CmdNotFoundModuleInterface.dll", + "PowerToys.CmdNotFound.dll", - "PowerToys.CropAndLockModuleInterface.dll", - "PowerToys.CropAndLock.exe", + "PowerToys.ColorPicker.dll", + "PowerToys.ColorPickerUI.dll", + "PowerToys.ColorPickerUI.exe", - "PowerToys.PowerOCRModuleInterface.dll", - "PowerToys.PowerOCR.dll", - "PowerToys.PowerOCR.exe", + "PowerToys.CropAndLockModuleInterface.dll", + "PowerToys.CropAndLock.exe", - "PowerToys.AdvancedPasteModuleInterface.dll", - "WinUI3Apps\\PowerToys.AdvancedPaste.exe", - "WinUI3Apps\\PowerToys.AdvancedPaste.dll", + "PowerToys.PowerOCRModuleInterface.dll", + "PowerToys.PowerOCR.dll", + "PowerToys.PowerOCR.exe", - "PowerToys.AwakeModuleInterface.dll", - "PowerToys.Awake.exe", - "PowerToys.Awake.dll", + "PowerToys.AdvancedPasteModuleInterface.dll", + "WinUI3Apps\\PowerToys.AdvancedPaste.exe", + "WinUI3Apps\\PowerToys.AdvancedPaste.dll", - "fancyzones.dll", - "PowerToys.FancyZonesEditor.exe", - "PowerToys.FancyZonesEditor.dll", - "PowerToys.FancyZonesEditorCommon.dll", - "PowerToys.FancyZonesModuleInterface.dll", - "PowerToys.FancyZones.exe", + "PowerToys.AwakeModuleInterface.dll", + "PowerToys.Awake.exe", + "PowerToys.Awake.dll", - "PowerToys.GcodePreviewHandler.dll", - "PowerToys.GcodePreviewHandler.exe", - "PowerToys.GcodePreviewHandlerCpp.dll", - "PowerToys.GcodeThumbnailProvider.dll", - "PowerToys.GcodeThumbnailProvider.exe", - "PowerToys.GcodeThumbnailProviderCpp.dll", - "PowerToys.ManagedTelemetry.dll", - "PowerToys.MarkdownPreviewHandler.dll", - "PowerToys.MarkdownPreviewHandler.exe", - "PowerToys.MarkdownPreviewHandlerCpp.dll", - "PowerToys.MonacoPreviewHandler.dll", - "PowerToys.MonacoPreviewHandler.exe", - "PowerToys.MonacoPreviewHandlerCpp.dll", - "PowerToys.PdfPreviewHandler.dll", - "PowerToys.PdfPreviewHandler.exe", - "PowerToys.PdfPreviewHandlerCpp.dll", - "PowerToys.PdfThumbnailProvider.dll", - "PowerToys.PdfThumbnailProvider.exe", - "PowerToys.PdfThumbnailProviderCpp.dll", - "PowerToys.powerpreview.dll", - "PowerToys.PreviewHandlerCommon.dll", - "PowerToys.QoiPreviewHandler.dll", - "PowerToys.QoiPreviewHandler.exe", - "PowerToys.QoiPreviewHandlerCpp.dll", - "PowerToys.QoiThumbnailProvider.dll", - "PowerToys.QoiThumbnailProvider.exe", - "PowerToys.QoiThumbnailProviderCpp.dll", - "PowerToys.StlThumbnailProvider.dll", - "PowerToys.StlThumbnailProvider.exe", - "PowerToys.StlThumbnailProviderCpp.dll", - "PowerToys.SvgPreviewHandler.dll", - "PowerToys.SvgPreviewHandler.exe", - "PowerToys.SvgPreviewHandlerCpp.dll", - "PowerToys.SvgThumbnailProvider.dll", - "PowerToys.SvgThumbnailProvider.exe", - "PowerToys.SvgThumbnailProviderCpp.dll", + "fancyzones.dll", + "PowerToys.FancyZonesEditor.exe", + "PowerToys.FancyZonesEditor.dll", + "PowerToys.FancyZonesEditorCommon.dll", + "PowerToys.FancyZonesModuleInterface.dll", + "PowerToys.FancyZones.exe", - "WinUI3Apps\\PowerToys.HostsModuleInterface.dll", - "WinUI3Apps\\PowerToys.HostsUILib.dll", - "WinUI3Apps\\PowerToys.Hosts.dll", - "WinUI3Apps\\PowerToys.Hosts.exe", + "PowerToys.GcodePreviewHandler.dll", + "PowerToys.GcodePreviewHandler.exe", + "PowerToys.GcodePreviewHandlerCpp.dll", + "PowerToys.GcodeThumbnailProvider.dll", + "PowerToys.GcodeThumbnailProvider.exe", + "PowerToys.GcodeThumbnailProviderCpp.dll", + "PowerToys.ManagedTelemetry.dll", + "PowerToys.MarkdownPreviewHandler.dll", + "PowerToys.MarkdownPreviewHandler.exe", + "PowerToys.MarkdownPreviewHandlerCpp.dll", + "PowerToys.MonacoPreviewHandler.dll", + "PowerToys.MonacoPreviewHandler.exe", + "PowerToys.MonacoPreviewHandlerCpp.dll", + "PowerToys.PdfPreviewHandler.dll", + "PowerToys.PdfPreviewHandler.exe", + "PowerToys.PdfPreviewHandlerCpp.dll", + "PowerToys.PdfThumbnailProvider.dll", + "PowerToys.PdfThumbnailProvider.exe", + "PowerToys.PdfThumbnailProviderCpp.dll", + "PowerToys.powerpreview.dll", + "PowerToys.PreviewHandlerCommon.dll", + "PowerToys.QoiPreviewHandler.dll", + "PowerToys.QoiPreviewHandler.exe", + "PowerToys.QoiPreviewHandlerCpp.dll", + "PowerToys.QoiThumbnailProvider.dll", + "PowerToys.QoiThumbnailProvider.exe", + "PowerToys.QoiThumbnailProviderCpp.dll", + "PowerToys.StlThumbnailProvider.dll", + "PowerToys.StlThumbnailProvider.exe", + "PowerToys.StlThumbnailProviderCpp.dll", + "PowerToys.SvgPreviewHandler.dll", + "PowerToys.SvgPreviewHandler.exe", + "PowerToys.SvgPreviewHandlerCpp.dll", + "PowerToys.SvgThumbnailProvider.dll", + "PowerToys.SvgThumbnailProvider.exe", + "PowerToys.SvgThumbnailProviderCpp.dll", - "WinUI3Apps\\PowerToys.FileLocksmithLib.Interop.dll", - "WinUI3Apps\\PowerToys.FileLocksmithExt.dll", - "WinUI3Apps\\PowerToys.FileLocksmithUI.exe", - "WinUI3Apps\\PowerToys.FileLocksmithUI.dll", - "WinUI3Apps\\PowerToys.FileLocksmithContextMenu.dll", - "FileLocksmithContextMenuPackage.msix", + "WinUI3Apps\\PowerToys.HostsModuleInterface.dll", + "WinUI3Apps\\PowerToys.HostsUILib.dll", + "WinUI3Apps\\PowerToys.Hosts.dll", + "WinUI3Apps\\PowerToys.Hosts.exe", - "WinUI3Apps\\Peek.Common.dll", - "WinUI3Apps\\Peek.FilePreviewer.dll", - "WinUI3Apps\\Powertoys.Peek.UI.dll", - "WinUI3Apps\\Powertoys.Peek.UI.exe", - "WinUI3Apps\\Powertoys.Peek.dll", + "WinUI3Apps\\PowerToys.FileLocksmithLib.Interop.dll", + "WinUI3Apps\\PowerToys.FileLocksmithExt.dll", + "WinUI3Apps\\PowerToys.FileLocksmithUI.exe", + "WinUI3Apps\\PowerToys.FileLocksmithUI.dll", + "WinUI3Apps\\PowerToys.FileLocksmithContextMenu.dll", + "FileLocksmithContextMenuPackage.msix", - "WinUI3Apps\\PowerToys.EnvironmentVariablesModuleInterface.dll", - "WinUI3Apps\\PowerToys.EnvironmentVariablesUILib.dll", - "WinUI3Apps\\PowerToys.EnvironmentVariables.dll", - "WinUI3Apps\\PowerToys.EnvironmentVariables.exe", + "WinUI3Apps\\Peek.Common.dll", + "WinUI3Apps\\Peek.FilePreviewer.dll", + "WinUI3Apps\\Powertoys.Peek.UI.dll", + "WinUI3Apps\\Powertoys.Peek.UI.exe", + "WinUI3Apps\\Powertoys.Peek.dll", - "PowerToys.ImageResizer.exe", - "PowerToys.ImageResizer.dll", - "PowerToys.ImageResizerExt.dll", - "PowerToys.ImageResizerContextMenu.dll", - "ImageResizerContextMenuPackage.msix", + "WinUI3Apps\\PowerToys.EnvironmentVariablesModuleInterface.dll", + "WinUI3Apps\\PowerToys.EnvironmentVariablesUILib.dll", + "WinUI3Apps\\PowerToys.EnvironmentVariables.dll", + "WinUI3Apps\\PowerToys.EnvironmentVariables.exe", - "PowerToys.KeyboardManager.dll", - "KeyboardManagerEditor\\PowerToys.KeyboardManagerEditor.exe", - "KeyboardManagerEngine\\PowerToys.KeyboardManagerEngine.exe", - "PowerToys.KeyboardManagerEditorLibraryWrapper.dll", + "PowerToys.ImageResizer.exe", + "PowerToys.ImageResizer.dll", + "PowerToys.ImageResizerExt.dll", + "PowerToys.ImageResizerContextMenu.dll", + "ImageResizerContextMenuPackage.msix", - "PowerToys.Launcher.dll", - "PowerToys.PowerLauncher.dll", - "PowerToys.PowerLauncher.exe", - "PowerToys.PowerLauncher.Telemetry.dll", - "Wox.dll", - "Wox.Infrastructure.dll", - "Wox.Plugin.dll", - "RunPlugins\\Calculator\\Microsoft.PowerToys.Run.Plugin.Calculator.dll", - "RunPlugins\\Folder\\Microsoft.Plugin.Folder.dll", - "RunPlugins\\Indexer\\Microsoft.Plugin.Indexer.dll", - "RunPlugins\\OneNote\\Microsoft.PowerToys.Run.Plugin.OneNote.dll", - "RunPlugins\\History\\Microsoft.PowerToys.Run.Plugin.History.dll", - "RunPlugins\\PowerToys\\Microsoft.PowerToys.Run.Plugin.PowerToys.dll", - "RunPlugins\\Program\\Microsoft.Plugin.Program.dll", - "RunPlugins\\Registry\\Microsoft.PowerToys.Run.Plugin.Registry.dll", - "RunPlugins\\WindowsSettings\\Microsoft.PowerToys.Run.Plugin.WindowsSettings.dll", - "RunPlugins\\Shell\\Microsoft.Plugin.Shell.dll", - "RunPlugins\\Uri\\Microsoft.Plugin.Uri.dll", - "RunPlugins\\WindowWalker\\Microsoft.Plugin.WindowWalker.dll", - "RunPlugins\\UnitConverter\\Community.PowerToys.Run.Plugin.UnitConverter.dll", - "RunPlugins\\VSCodeWorkspaces\\Community.PowerToys.Run.Plugin.VSCodeWorkspaces.dll", - "RunPlugins\\Service\\Microsoft.PowerToys.Run.Plugin.Service.dll", - "RunPlugins\\System\\Microsoft.PowerToys.Run.Plugin.System.dll", - "RunPlugins\\TimeDate\\Microsoft.PowerToys.Run.Plugin.TimeDate.dll", - "RunPlugins\\ValueGenerator\\Community.PowerToys.Run.Plugin.ValueGenerator.dll", - "RunPlugins\\WebSearch\\Community.PowerToys.Run.Plugin.WebSearch.dll", - "RunPlugins\\WindowsTerminal\\Microsoft.PowerToys.Run.Plugin.WindowsTerminal.dll", - - "WinUI3Apps\\PowerToys.MeasureToolModuleInterface.dll", - "WinUI3Apps\\PowerToys.MeasureToolCore.dll", - "WinUI3Apps\\PowerToys.MeasureToolUI.dll", - "WinUI3Apps\\PowerToys.MeasureToolUI.exe", + "PowerToys.KeyboardManager.dll", + "KeyboardManagerEditor\\PowerToys.KeyboardManagerEditor.exe", + "KeyboardManagerEngine\\PowerToys.KeyboardManagerEngine.exe", + "PowerToys.KeyboardManagerEditorLibraryWrapper.dll", - "PowerToys.FindMyMouse.dll", - "PowerToys.MouseHighlighter.dll", - "PowerToys.MouseJump.dll", - "PowerToys.MouseJump.Common.dll", - "PowerToys.MousePointerCrosshairs.dll", - "PowerToys.MouseJumpUI.dll", - "PowerToys.MouseJumpUI.exe", + "PowerToys.Launcher.dll", + "PowerToys.PowerLauncher.dll", + "PowerToys.PowerLauncher.exe", + "PowerToys.PowerLauncher.Telemetry.dll", + "Wox.dll", + "Wox.Infrastructure.dll", + "Wox.Plugin.dll", + "RunPlugins\\Calculator\\Microsoft.PowerToys.Run.Plugin.Calculator.dll", + "RunPlugins\\Folder\\Microsoft.Plugin.Folder.dll", + "RunPlugins\\Indexer\\Microsoft.Plugin.Indexer.dll", + "RunPlugins\\OneNote\\Microsoft.PowerToys.Run.Plugin.OneNote.dll", + "RunPlugins\\History\\Microsoft.PowerToys.Run.Plugin.History.dll", + "RunPlugins\\PowerToys\\Microsoft.PowerToys.Run.Plugin.PowerToys.dll", + "RunPlugins\\Program\\Microsoft.Plugin.Program.dll", + "RunPlugins\\Registry\\Microsoft.PowerToys.Run.Plugin.Registry.dll", + "RunPlugins\\WindowsSettings\\Microsoft.PowerToys.Run.Plugin.WindowsSettings.dll", + "RunPlugins\\Shell\\Microsoft.Plugin.Shell.dll", + "RunPlugins\\Uri\\Microsoft.Plugin.Uri.dll", + "RunPlugins\\WindowWalker\\Microsoft.Plugin.WindowWalker.dll", + "RunPlugins\\UnitConverter\\Community.PowerToys.Run.Plugin.UnitConverter.dll", + "RunPlugins\\VSCodeWorkspaces\\Community.PowerToys.Run.Plugin.VSCodeWorkspaces.dll", + "RunPlugins\\Service\\Microsoft.PowerToys.Run.Plugin.Service.dll", + "RunPlugins\\System\\Microsoft.PowerToys.Run.Plugin.System.dll", + "RunPlugins\\TimeDate\\Microsoft.PowerToys.Run.Plugin.TimeDate.dll", + "RunPlugins\\ValueGenerator\\Community.PowerToys.Run.Plugin.ValueGenerator.dll", + "RunPlugins\\WebSearch\\Community.PowerToys.Run.Plugin.WebSearch.dll", + "RunPlugins\\WindowsTerminal\\Microsoft.PowerToys.Run.Plugin.WindowsTerminal.dll", - "PowerToys.MouseWithoutBorders.dll", - "PowerToys.MouseWithoutBorders.exe", - "PowerToys.MouseWithoutBordersModuleInterface.dll", - "PowerToys.MouseWithoutBordersService.dll", - "PowerToys.MouseWithoutBordersService.exe", - "PowerToys.MouseWithoutBordersHelper.dll", - "PowerToys.MouseWithoutBordersHelper.exe", + "WinUI3Apps\\PowerToys.MeasureToolModuleInterface.dll", + "WinUI3Apps\\PowerToys.MeasureToolCore.dll", + "WinUI3Apps\\PowerToys.MeasureToolUI.dll", + "WinUI3Apps\\PowerToys.MeasureToolUI.exe", - "WinUI3Apps\\PowerToys.NewPlus.ShellExtension.dll", - "WinUI3Apps\\NewPlusPackage.msix", - "WinUI3Apps\\PowerToys.NewPlus.ShellExtension.win10.dll", + "PowerToys.FindMyMouse.dll", + "PowerToys.MouseHighlighter.dll", + "PowerToys.MouseJump.dll", + "PowerToys.MouseJump.Common.dll", + "PowerToys.MousePointerCrosshairs.dll", + "PowerToys.MouseJumpUI.dll", + "PowerToys.MouseJumpUI.exe", - "PowerAccent.Core.dll", - "PowerToys.PowerAccent.dll", - "PowerToys.PowerAccent.exe", - "PowerToys.PowerAccentModuleInterface.dll", - "PowerToys.PowerAccentKeyboardService.dll", + "PowerToys.MouseWithoutBorders.dll", + "PowerToys.MouseWithoutBorders.exe", + "PowerToys.MouseWithoutBordersModuleInterface.dll", + "PowerToys.MouseWithoutBordersService.dll", + "PowerToys.MouseWithoutBordersService.exe", + "PowerToys.MouseWithoutBordersHelper.dll", + "PowerToys.MouseWithoutBordersHelper.exe", - "WinUI3Apps\\PowerToys.PowerRenameExt.dll", - "WinUI3Apps\\PowerToys.PowerRename.exe", - "WinUI3Apps\\PowerToys.PowerRenameContextMenu.dll", - "WinUI3Apps\\PowerRenameContextMenuPackage.msix", + "WinUI3Apps\\PowerToys.NewPlus.ShellExtension.dll", + "WinUI3Apps\\NewPlusPackage.msix", + "WinUI3Apps\\PowerToys.NewPlus.ShellExtension.win10.dll", - "PowerToys.WorkspacesSnapshotTool.exe", - "PowerToys.WorkspacesLauncher.exe", - "PowerToys.WorkspacesWindowArranger.exe", - "PowerToys.WorkspacesEditor.exe", - "PowerToys.WorkspacesEditor.dll", - "PowerToys.WorkspacesLauncherUI.exe", - "PowerToys.WorkspacesLauncherUI.dll", - "PowerToys.WorkspacesModuleInterface.dll", - "PowerToys.WorkspacesCsharpLibrary.dll", + "PowerAccent.Core.dll", + "PowerToys.PowerAccent.dll", + "PowerToys.PowerAccent.exe", + "PowerToys.PowerAccentModuleInterface.dll", + "PowerToys.PowerAccentKeyboardService.dll", - "WinUI3Apps\\PowerToys.RegistryPreviewExt.dll", - "WinUI3Apps\\PowerToys.RegistryPreviewUILib.dll", - "WinUI3Apps\\PowerToys.RegistryPreview.dll", - "WinUI3Apps\\PowerToys.RegistryPreview.exe", + "WinUI3Apps\\PowerToys.PowerRenameExt.dll", + "WinUI3Apps\\PowerToys.PowerRename.exe", + "WinUI3Apps\\PowerToys.PowerRenameContextMenu.dll", + "WinUI3Apps\\PowerRenameContextMenuPackage.msix", - "PowerToys.ShortcutGuide.exe", - "PowerToys.ShortcutGuideModuleInterface.dll", + "PowerToys.WorkspacesSnapshotTool.exe", + "PowerToys.WorkspacesLauncher.exe", + "PowerToys.WorkspacesWindowArranger.exe", + "PowerToys.WorkspacesEditor.exe", + "PowerToys.WorkspacesEditor.dll", + "PowerToys.WorkspacesLauncherUI.exe", + "PowerToys.WorkspacesLauncherUI.dll", + "PowerToys.WorkspacesModuleInterface.dll", + "PowerToys.WorkspacesCsharpLibrary.dll", - "PowerToys.ZoomIt.exe", - "PowerToys.ZoomItModuleInterface.dll", - "PowerToys.ZoomItSettingsInterop.dll", + "WinUI3Apps\\PowerToys.RegistryPreviewExt.dll", + "WinUI3Apps\\PowerToys.RegistryPreviewUILib.dll", + "WinUI3Apps\\PowerToys.RegistryPreview.dll", + "WinUI3Apps\\PowerToys.RegistryPreview.exe", - "WinUI3Apps\\PowerToys.Settings.dll", - "WinUI3Apps\\PowerToys.Settings.exe", + "PowerToys.ShortcutGuide.exe", + "PowerToys.ShortcutGuideModuleInterface.dll", - "PowerToys.CmdPalModuleInterface.dll", - "CmdPalKeyboardService.dll", - "*Microsoft.CmdPal.UI_*.msix" - ], + "PowerToys.ZoomIt.exe", + "PowerToys.ZoomItModuleInterface.dll", + "PowerToys.ZoomItSettingsInterop.dll", + + "WinUI3Apps\\PowerToys.Settings.dll", + "WinUI3Apps\\PowerToys.Settings.exe", + + "PowerToys.CmdPalModuleInterface.dll", + "CmdPalKeyboardService.dll", + "*Microsoft.CmdPal.UI_*.msix" + ], "SigningInfo": { "Operations": [ { diff --git a/NOTICE.md b/NOTICE.md index fb2450b66a..2b94d67a4b 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -79,6 +79,43 @@ For more information, please refer to ### Calculator +#### exprtk + +We use the exprtk library (exprtk.hpp) to evaluate mathematical expressions. + +**Source**: [https://github.com/ArashPartow/exprtk](https://github.com/ArashPartow/exprtk) + +``` +MIT License + +Copyright (c) 1999-2024 Arash Partow + +https://www.partow.net/programming/exprtk/index.html + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +``` + +## Utility: PowerToys Run Built-in Extensions + +### Calculator + #### Mages We use the Mages NuGet package for calculating the result of expression. diff --git a/PowerToys.sln b/PowerToys.sln index fdf0be72f8..f58aa7c077 100644 --- a/PowerToys.sln +++ b/PowerToys.sln @@ -717,6 +717,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CmdPalKeyboardService", "sr EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerRename.FuzzingTest", "src\modules\powerrename\PowerRename.FuzzingTest\PowerRename.FuzzingTest.vcxproj", "{2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CalculatorEngineCommon", "src\common\CalculatorEngineCommon\CalculatorEngineCommon.vcxproj", "{2CF78CF7-8FEB-4BE1-9591-55FA25B48FC6}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM64 = Debug|ARM64 @@ -2619,6 +2621,14 @@ Global {2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}.Release|ARM64.ActiveCfg = Release|ARM64 {2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}.Release|x64.ActiveCfg = Release|x64 {2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}.Release|x64.Build.0 = Release|x64 + {2CF78CF7-8FEB-4BE1-9591-55FA25B48FC6}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {2CF78CF7-8FEB-4BE1-9591-55FA25B48FC6}.Debug|ARM64.Build.0 = Debug|ARM64 + {2CF78CF7-8FEB-4BE1-9591-55FA25B48FC6}.Debug|x64.ActiveCfg = Debug|x64 + {2CF78CF7-8FEB-4BE1-9591-55FA25B48FC6}.Debug|x64.Build.0 = Debug|x64 + {2CF78CF7-8FEB-4BE1-9591-55FA25B48FC6}.Release|ARM64.ActiveCfg = Release|ARM64 + {2CF78CF7-8FEB-4BE1-9591-55FA25B48FC6}.Release|ARM64.Build.0 = Release|ARM64 + {2CF78CF7-8FEB-4BE1-9591-55FA25B48FC6}.Release|x64.ActiveCfg = Release|x64 + {2CF78CF7-8FEB-4BE1-9591-55FA25B48FC6}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2893,6 +2903,7 @@ Global {0217E86E-3476-9946-DE8E-9D200CEBD47A} = {D1D6BC88-09AE-4FB4-AD24-5DED46A791DD} {5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2} = {3846508C-77EB-4034-A702-F8BB263C4F79} {2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E} = {89E20BCE-EB9C-46C8-8B50-E01A82E6FDC3} + {2CF78CF7-8FEB-4BE1-9591-55FA25B48FC6} = {1AFB6476-670D-4E80-A464-657E01DFF482} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0} diff --git a/src/common/CalculatorEngineCommon/Calculator.cpp b/src/common/CalculatorEngineCommon/Calculator.cpp new file mode 100644 index 0000000000..0d96945560 --- /dev/null +++ b/src/common/CalculatorEngineCommon/Calculator.cpp @@ -0,0 +1,24 @@ +#include "pch.h" +#include "Calculator.h" +#include "Calculator.g.cpp" +#include "ExprtkEvaluator.h" + +namespace winrt::CalculatorEngineCommon::implementation +{ + Calculator::Calculator(winrt::Windows::Foundation::Collections::IPropertySet const& constants) + { + for (auto const& pair : constants) + { + auto key = pair.Key(); + auto value = winrt::unbox_value(pair.Value()); + m_constants.emplace(winrt::to_string(key), value); + } + } + + hstring Calculator::EvaluateExpression(hstring const& expression) + { + auto result = ExprtkCalculator::internal::EvaluateExpression(winrt::to_string(expression), m_constants); + + return hstring(result); + } +} diff --git a/src/common/CalculatorEngineCommon/Calculator.h b/src/common/CalculatorEngineCommon/Calculator.h new file mode 100644 index 0000000000..abf2a4ec0d --- /dev/null +++ b/src/common/CalculatorEngineCommon/Calculator.h @@ -0,0 +1,25 @@ +#pragma once + +#include "Calculator.g.h" + +namespace winrt::CalculatorEngineCommon::implementation +{ + struct Calculator : CalculatorT + { + Calculator() = default; + + Calculator(winrt::Windows::Foundation::Collections::IPropertySet const& constants); + + winrt::hstring EvaluateExpression(winrt::hstring const& expression); + + private: + std::unordered_map m_constants; + }; +} + +namespace winrt::CalculatorEngineCommon::factory_implementation +{ + struct Calculator : CalculatorT + { + }; +} diff --git a/src/common/CalculatorEngineCommon/Calculator.idl b/src/common/CalculatorEngineCommon/Calculator.idl new file mode 100644 index 0000000000..241a517a7d --- /dev/null +++ b/src/common/CalculatorEngineCommon/Calculator.idl @@ -0,0 +1,10 @@ +namespace CalculatorEngineCommon +{ + [default_interface] + runtimeclass Calculator + { + Calculator(); + Calculator(Windows.Foundation.Collections.IPropertySet constants); + String EvaluateExpression(String expression); + } +} diff --git a/src/common/CalculatorEngineCommon/CalculatorEngineCommon.def b/src/common/CalculatorEngineCommon/CalculatorEngineCommon.def new file mode 100644 index 0000000000..24e7c1235c --- /dev/null +++ b/src/common/CalculatorEngineCommon/CalculatorEngineCommon.def @@ -0,0 +1,3 @@ +EXPORTS +DllCanUnloadNow = WINRT_CanUnloadNow PRIVATE +DllGetActivationFactory = WINRT_GetActivationFactory PRIVATE diff --git a/src/common/CalculatorEngineCommon/CalculatorEngineCommon.vcxproj b/src/common/CalculatorEngineCommon/CalculatorEngineCommon.vcxproj new file mode 100644 index 0000000000..ddc4a970bc --- /dev/null +++ b/src/common/CalculatorEngineCommon/CalculatorEngineCommon.vcxproj @@ -0,0 +1,181 @@ + + + + + true + true + {2cf78cf7-8feb-4be1-9591-55fa25b48fc6} + CalculatorEngineCommon + CalculatorEngineCommon + false + + + + true + false + + + + + true + true + en-US + 17.0 + 10.0 + + + true + true + true + Windows Store + false + + + + + <_VC_Target_Library_Platform>Desktop + <_NoWinAPIFamilyApp>true + + + + + DynamicLibrary + v143 + Unicode + false + + + true + true + + + false + true + false + + + + + + + + + + + + + + CalculatorEngineCommon + ..\..\..\$(Platform)\$(Configuration)\ + + + + $(IntDir)pch.pch + Level4 + %(AdditionalOptions) /bigobj + _WINRT_DLL;WINRT_LEAN_AND_MEAN;%(PreprocessorDefinitions) + ../../..;%(AdditionalIncludeDirectories) + $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories) + + + Console + false + CalculatorEngineCommon.def + Shell32.lib;user32.lib;%(AdditionalDependencies) + + + + + _DEBUG;%(PreprocessorDefinitions) + + + + + NDEBUG;%(PreprocessorDefinitions) + + + true + true + + + + + + + + Calculator.idl + + + + + NotUsing + + + Create + + + Calculator.idl + + + + + + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + + + + MultiThreadedDebug + stdcpp17 + + + + %(IgnoreSpecificDefaultLibraries);libucrtd.lib + %(AdditionalOptions) /defaultlib:ucrtd.lib + + + + + + MultiThreaded + + + + %(IgnoreSpecificDefaultLibraries);libucrt.lib + %(AdditionalOptions) /defaultlib:ucrt.lib + + + + \ No newline at end of file diff --git a/src/common/CalculatorEngineCommon/CalculatorEngineCommon.vcxproj.filters b/src/common/CalculatorEngineCommon/CalculatorEngineCommon.vcxproj.filters new file mode 100644 index 0000000000..6b84b33112 --- /dev/null +++ b/src/common/CalculatorEngineCommon/CalculatorEngineCommon.vcxproj.filters @@ -0,0 +1,29 @@ + + + + + accd3aa8-1ba0-4223-9bbe-0c431709210b + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tga;tiff;tif;png;wav;mfcribbon-ms + + + {926ab91d-31b4-48c3-b9a4-e681349f27f0} + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/common/CalculatorEngineCommon/ExprtkEvaluator.cpp b/src/common/CalculatorEngineCommon/ExprtkEvaluator.cpp new file mode 100644 index 0000000000..dbc2120b24 --- /dev/null +++ b/src/common/CalculatorEngineCommon/ExprtkEvaluator.cpp @@ -0,0 +1,50 @@ +#include "ExprtkEvaluator.h" +#include "exprtk.hpp" +#include +#include +#include + +namespace ExprtkCalculator::internal +{ + + std::wstring ToWStringFullPrecision(double value) + { + std::wostringstream oss; + oss << std::fixed << std::setprecision(15) << value; + return oss.str(); + } + + std::wstring EvaluateExpression( + const std::string& expressionText, + const std::unordered_map& constants) + { + exprtk::symbol_table symbol_table; + + for (auto const& [name, value] : constants) + { + symbol_table.add_constant(name, value); + } + + exprtk::expression expression; + expression.register_symbol_table(symbol_table); + + exprtk::parser parser; + + // Enable all base functions and arithmetic operators + parser.settings().enable_all_base_functions(); // Enable all base functions like sin, cos, log, etc. + parser.settings().enable_all_arithmetic_ops(); // Enable all arithmetic operators like +, -, *, /, etc. + + // Disable all control structures and assignment operators to ensure only expressions are evaluated + parser.settings().disable_all_control_structures(); // Disable control structures like if, for, while, etc. + parser.settings().disable_all_assignment_ops(); // Disable assignment operators like =, +=, -=, etc. + + // Disabled for now, but can be enabled later for enhanced functionality + parser.settings().disable_all_logic_ops(); // Disable logical operators like &&, ||, !, etc. + parser.settings().disable_all_inequality_ops(); // Disable inequality operators like <, >, <=, >=, !=, etc. + + if (!parser.compile(expressionText, expression)) + return L"NaN"; + + return ToWStringFullPrecision(expression.value()); + } +} \ No newline at end of file diff --git a/src/common/CalculatorEngineCommon/ExprtkEvaluator.h b/src/common/CalculatorEngineCommon/ExprtkEvaluator.h new file mode 100644 index 0000000000..bd09774b96 --- /dev/null +++ b/src/common/CalculatorEngineCommon/ExprtkEvaluator.h @@ -0,0 +1,10 @@ +#pragma once +#include +#include + +namespace ExprtkCalculator::internal +{ + std::wstring EvaluateExpression( + const std::string& expression, + const std::unordered_map& constants); +} \ No newline at end of file diff --git a/src/common/CalculatorEngineCommon/PropertySheet.props b/src/common/CalculatorEngineCommon/PropertySheet.props new file mode 100644 index 0000000000..e34141b019 --- /dev/null +++ b/src/common/CalculatorEngineCommon/PropertySheet.props @@ -0,0 +1,16 @@ + + + + + + + + \ No newline at end of file diff --git a/src/common/CalculatorEngineCommon/exprtk.hpp b/src/common/CalculatorEngineCommon/exprtk.hpp new file mode 100644 index 0000000000..6ad76542f4 --- /dev/null +++ b/src/common/CalculatorEngineCommon/exprtk.hpp @@ -0,0 +1,46251 @@ +/* + ****************************************************************** + * C++ Mathematical Expression Toolkit Library * + * * + * Author: Arash Partow (1999-2024) * + * URL: https://www.partow.net/programming/exprtk/index.html * + * * + * Copyright notice: * + * Free use of the C++ Mathematical Expression Toolkit Library is * + * permitted under the guidelines and in accordance with the most * + * current version of the MIT License. * + * https://www.opensource.org/licenses/MIT * + * SPDX-License-Identifier: MIT * + * * + * Example expressions: * + * (00) (y + x / y) * (x - y / x) * + * (01) (x^2 / sin(2 * pi / y)) - x / 2 * + * (02) sqrt(1 - (x^2)) * + * (03) 1 - sin(2 * x) + cos(pi / y) * + * (04) a * exp(2 * t) + c * + * (05) if(((x + 2) == 3) and ((y + 5) <= 9), 1 + w, 2 / z) * + * (06) (avg(x,y) <= x + y ? x - y : x * y) + 2 * pi / x * + * (07) z := x + sin(2 * pi / y) * + * (08) u := 2 * (pi * z) / (w := x + cos(y / pi)) * + * (09) clamp(-1, sin(2 * pi * x) + cos(y / 2 * pi), +1) * + * (10) inrange(-2, m, +2) == if(({-2 <= m} and [m <= +2]), 1, 0) * + * (11) (2sin(x)cos(2y)7 + 1) == (2 * sin(x) * cos(2*y) * 7 + 1) * + * (12) (x ilike 's*ri?g') and [y < (3 z^7 + w)] * + * * + ****************************************************************** +*/ + +#pragma system_header + +#ifndef INCLUDE_EXPRTK_HPP +#define INCLUDE_EXPRTK_HPP + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace exprtk +{ + #ifdef exprtk_enable_debugging + #define exprtk_debug(params) printf params + #else + #define exprtk_debug(params) (void)0 + #endif + + #define exprtk_error_location \ + "exprtk.hpp:" + details::to_str(__LINE__) \ + + #if __cplusplus >= 201103L + #define exprtk_override override + #define exprtk_final final + #define exprtk_delete = delete + #else + #define exprtk_override + #define exprtk_final + #define exprtk_delete + #endif + + #if __cplusplus >= 201603L + #define exprtk_fallthrough [[fallthrough]]; + #elif (__cplusplus >= 201103L) && (defined(__GNUC__) && !defined(__clang__)) + #define exprtk_fallthrough [[gnu::fallthrough]]; + #else + #ifndef _MSC_VER + #define exprtk_fallthrough __attribute__ ((fallthrough)); + #else + #define exprtk_fallthrough + #endif + #endif + + namespace details + { + typedef char char_t; + typedef char_t* char_ptr; + typedef char_t const* char_cptr; + typedef unsigned char uchar_t; + typedef uchar_t* uchar_ptr; + typedef uchar_t const* uchar_cptr; + typedef unsigned long long int _uint64_t; + typedef long long int _int64_t; + + inline bool is_whitespace(const char_t c) + { + return (' ' == c) || ('\n' == c) || + ('\r' == c) || ('\t' == c) || + ('\b' == c) || ('\v' == c) || + ('\f' == c) ; + } + + inline bool is_operator_char(const char_t c) + { + return ('+' == c) || ('-' == c) || + ('*' == c) || ('/' == c) || + ('^' == c) || ('<' == c) || + ('>' == c) || ('=' == c) || + (',' == c) || ('!' == c) || + ('(' == c) || (')' == c) || + ('[' == c) || (']' == c) || + ('{' == c) || ('}' == c) || + ('%' == c) || (':' == c) || + ('?' == c) || ('&' == c) || + ('|' == c) || (';' == c) ; + } + + inline bool is_letter(const char_t c) + { + return (('a' <= c) && (c <= 'z')) || + (('A' <= c) && (c <= 'Z')) ; + } + + inline bool is_digit(const char_t c) + { + return ('0' <= c) && (c <= '9'); + } + + inline bool is_letter_or_digit(const char_t c) + { + return is_letter(c) || is_digit(c); + } + + inline bool is_left_bracket(const char_t c) + { + return ('(' == c) || ('[' == c) || ('{' == c); + } + + inline bool is_right_bracket(const char_t c) + { + return (')' == c) || (']' == c) || ('}' == c); + } + + inline bool is_bracket(const char_t c) + { + return is_left_bracket(c) || is_right_bracket(c); + } + + inline bool is_sign(const char_t c) + { + return ('+' == c) || ('-' == c); + } + + inline bool is_invalid(const char_t c) + { + return !is_whitespace (c) && + !is_operator_char(c) && + !is_letter (c) && + !is_digit (c) && + ('.' != c) && + ('_' != c) && + ('$' != c) && + ('~' != c) && + ('\'' != c); + } + + inline bool is_valid_string_char(const char_t c) + { + return std::isprint(static_cast(c)) || + is_whitespace(c); + } + + #ifndef exprtk_disable_caseinsensitivity + inline void case_normalise(std::string& s) + { + for (std::size_t i = 0; i < s.size(); ++i) + { + s[i] = static_cast(std::tolower(s[i])); + } + } + + inline bool imatch(const char_t c1, const char_t c2) + { + return std::tolower(c1) == std::tolower(c2); + } + + inline bool imatch(const std::string& s1, const std::string& s2) + { + if (s1.size() == s2.size()) + { + for (std::size_t i = 0; i < s1.size(); ++i) + { + if (std::tolower(s1[i]) != std::tolower(s2[i])) + { + return false; + } + } + + return true; + } + + return false; + } + + struct ilesscompare + { + inline bool operator() (const std::string& s1, const std::string& s2) const + { + const std::size_t length = std::min(s1.size(),s2.size()); + + for (std::size_t i = 0; i < length; ++i) + { + const char_t c1 = static_cast(std::tolower(s1[i])); + const char_t c2 = static_cast(std::tolower(s2[i])); + + if (c1 < c2) + return true; + else if (c2 < c1) + return false; + } + + return s1.size() < s2.size(); + } + }; + + #else + inline void case_normalise(std::string&) + {} + + inline bool imatch(const char_t c1, const char_t c2) + { + return c1 == c2; + } + + inline bool imatch(const std::string& s1, const std::string& s2) + { + return s1 == s2; + } + + struct ilesscompare + { + inline bool operator() (const std::string& s1, const std::string& s2) const + { + return s1 < s2; + } + }; + #endif + + inline bool is_valid_sf_symbol(const std::string& symbol) + { + // Special function: $f12 or $F34 + return (4 == symbol.size()) && + ('$' == symbol[0]) && + imatch('f',symbol[1]) && + is_digit(symbol[2]) && + is_digit(symbol[3]); + } + + inline const char_t& front(const std::string& s) + { + return s[0]; + } + + inline const char_t& back(const std::string& s) + { + return s[s.size() - 1]; + } + + template + inline std::string to_str_impl(SignedType i) + { + if (0 == i) + return std::string("0"); + + std::string result; + + const int sign = (i < 0) ? -1 : 1; + + for ( ; i; i /= 10) + { + result += '0' + static_cast(sign * (i % 10)); + } + + if (sign < 0) + { + result += '-'; + } + + std::reverse(result.begin(), result.end()); + + return result; + } + + inline std::string to_str(int i) + { + return to_str_impl(i); + } + + inline std::string to_str(std::size_t i) + { + return to_str_impl(static_cast(i)); + } + + inline bool is_hex_digit(const uchar_t digit) + { + return (('0' <= digit) && (digit <= '9')) || + (('A' <= digit) && (digit <= 'F')) || + (('a' <= digit) && (digit <= 'f')) ; + } + + inline uchar_t hex_to_bin(uchar_t h) + { + if (('0' <= h) && (h <= '9')) + return (h - '0'); + else + return static_cast(std::toupper(h) - 'A' + 10); + } + + template + inline bool parse_hex(Iterator& itr, Iterator end, + char_t& result) + { + if ( + (end == (itr )) || + (end == (itr + 1)) || + (end == (itr + 2)) || + (end == (itr + 3)) || + ('0' != *(itr )) || + ('X' != std::toupper(*(itr + 1))) || + (!is_hex_digit(*(itr + 2))) || + (!is_hex_digit(*(itr + 3))) + ) + { + return false; + } + + result = hex_to_bin(static_cast(*(itr + 2))) << 4 | + hex_to_bin(static_cast(*(itr + 3))) ; + + return true; + } + + inline bool cleanup_escapes(std::string& s) + { + typedef std::string::iterator str_itr_t; + + str_itr_t itr1 = s.begin(); + str_itr_t itr2 = s.begin(); + str_itr_t end = s.end (); + + std::size_t removal_count = 0; + + while (end != itr1) + { + if ('\\' == (*itr1)) + { + if (end == ++itr1) + { + return false; + } + else if (parse_hex(itr1, end, *itr2)) + { + itr1 += 4; + itr2 += 1; + removal_count += 4; + } + else if ('a' == (*itr1)) { (*itr2++) = '\a'; ++itr1; ++removal_count; } + else if ('b' == (*itr1)) { (*itr2++) = '\b'; ++itr1; ++removal_count; } + else if ('f' == (*itr1)) { (*itr2++) = '\f'; ++itr1; ++removal_count; } + else if ('n' == (*itr1)) { (*itr2++) = '\n'; ++itr1; ++removal_count; } + else if ('r' == (*itr1)) { (*itr2++) = '\r'; ++itr1; ++removal_count; } + else if ('t' == (*itr1)) { (*itr2++) = '\t'; ++itr1; ++removal_count; } + else if ('v' == (*itr1)) { (*itr2++) = '\v'; ++itr1; ++removal_count; } + else if ('0' == (*itr1)) { (*itr2++) = '\0'; ++itr1; ++removal_count; } + else + { + (*itr2++) = (*itr1++); + ++removal_count; + } + + continue; + } + else + (*itr2++) = (*itr1++); + } + + if ((removal_count > s.size()) || (0 == removal_count)) + return false; + + s.resize(s.size() - removal_count); + + return true; + } + + class build_string + { + public: + + explicit build_string(const std::size_t& initial_size = 64) + { + data_.reserve(initial_size); + } + + inline build_string& operator << (const std::string& s) + { + data_ += s; + return (*this); + } + + inline build_string& operator << (char_cptr s) + { + data_ += std::string(s); + return (*this); + } + + inline operator std::string () const + { + return data_; + } + + inline std::string as_string() const + { + return data_; + } + + private: + + std::string data_; + }; + + static const std::string reserved_words[] = + { + "assert", "break", "case", "continue", "const", "default", + "false", "for", "if", "else", "ilike", "in", "like", "and", + "nand", "nor", "not", "null", "or", "repeat", "return", + "shl", "shr", "swap", "switch", "true", "until", "var", + "while", "xnor", "xor", "&", "|" + }; + + static const std::size_t reserved_words_size = sizeof(reserved_words) / sizeof(std::string); + + static const std::string reserved_symbols[] = + { + "abs", "acos", "acosh", "and", "asin", "asinh", "assert", + "atan", "atanh", "atan2", "avg", "break", "case", "ceil", + "clamp", "continue", "const", "cos", "cosh", "cot", "csc", + "default", "deg2grad", "deg2rad", "equal", "erf", "erfc", + "exp", "expm1", "false", "floor", "for", "frac", "grad2deg", + "hypot", "iclamp", "if", "else", "ilike", "in", "inrange", + "like", "log", "log10", "log2", "logn", "log1p", "mand", + "max", "min", "mod", "mor", "mul", "ncdf", "nand", "nor", + "not", "not_equal", "null", "or", "pow", "rad2deg", + "repeat", "return", "root", "round", "roundn", "sec", "sgn", + "shl", "shr", "sin", "sinc", "sinh", "sqrt", "sum", "swap", + "switch", "tan", "tanh", "true", "trunc", "until", "var", + "while", "xnor", "xor", "&", "|" + }; + + static const std::size_t reserved_symbols_size = sizeof(reserved_symbols) / sizeof(std::string); + + static const std::string base_function_list[] = + { + "abs", "acos", "acosh", "asin", "asinh", "atan", "atanh", + "atan2", "avg", "ceil", "clamp", "cos", "cosh", "cot", + "csc", "equal", "erf", "erfc", "exp", "expm1", "floor", + "frac", "hypot", "iclamp", "like", "log", "log10", "log2", + "logn", "log1p", "mand", "max", "min", "mod", "mor", "mul", + "ncdf", "pow", "root", "round", "roundn", "sec", "sgn", + "sin", "sinc", "sinh", "sqrt", "sum", "swap", "tan", "tanh", + "trunc", "not_equal", "inrange", "deg2grad", "deg2rad", + "rad2deg", "grad2deg" + }; + + static const std::size_t base_function_list_size = sizeof(base_function_list) / sizeof(std::string); + + static const std::string logic_ops_list[] = + { + "and", "nand", "nor", "not", "or", "xnor", "xor", "&", "|" + }; + + static const std::size_t logic_ops_list_size = sizeof(logic_ops_list) / sizeof(std::string); + + static const std::string cntrl_struct_list[] = + { + "if", "switch", "for", "while", "repeat", "return" + }; + + static const std::size_t cntrl_struct_list_size = sizeof(cntrl_struct_list) / sizeof(std::string); + + static const std::string arithmetic_ops_list[] = + { + "+", "-", "*", "/", "%", "^" + }; + + static const std::size_t arithmetic_ops_list_size = sizeof(arithmetic_ops_list) / sizeof(std::string); + + static const std::string assignment_ops_list[] = + { + ":=", "+=", "-=", + "*=", "/=", "%=" + }; + + static const std::size_t assignment_ops_list_size = sizeof(assignment_ops_list) / sizeof(std::string); + + static const std::string inequality_ops_list[] = + { + "<", "<=", "==", + "=", "!=", "<>", + ">=", ">" + }; + + static const std::size_t inequality_ops_list_size = sizeof(inequality_ops_list) / sizeof(std::string); + + inline bool is_reserved_word(const std::string& symbol) + { + for (std::size_t i = 0; i < reserved_words_size; ++i) + { + if (imatch(symbol, reserved_words[i])) + { + return true; + } + } + + return false; + } + + inline bool is_reserved_symbol(const std::string& symbol) + { + for (std::size_t i = 0; i < reserved_symbols_size; ++i) + { + if (imatch(symbol, reserved_symbols[i])) + { + return true; + } + } + + return false; + } + + inline bool is_base_function(const std::string& function_name) + { + for (std::size_t i = 0; i < base_function_list_size; ++i) + { + if (imatch(function_name, base_function_list[i])) + { + return true; + } + } + + return false; + } + + inline bool is_control_struct(const std::string& cntrl_strct) + { + for (std::size_t i = 0; i < cntrl_struct_list_size; ++i) + { + if (imatch(cntrl_strct, cntrl_struct_list[i])) + { + return true; + } + } + + return false; + } + + inline bool is_logic_opr(const std::string& lgc_opr) + { + for (std::size_t i = 0; i < logic_ops_list_size; ++i) + { + if (imatch(lgc_opr, logic_ops_list[i])) + { + return true; + } + } + + return false; + } + + struct cs_match + { + static inline bool cmp(const char_t c0, const char_t c1) + { + return (c0 == c1); + } + }; + + struct cis_match + { + static inline bool cmp(const char_t c0, const char_t c1) + { + return (std::tolower(c0) == std::tolower(c1)); + } + }; + + template + inline bool match_impl(const Iterator pattern_begin, + const Iterator pattern_end , + const Iterator data_begin , + const Iterator data_end , + const typename std::iterator_traits::value_type& zero_or_more, + const typename std::iterator_traits::value_type& exactly_one ) + { + typedef typename std::iterator_traits::value_type type; + + const Iterator null_itr(0); + + Iterator p_itr = pattern_begin; + Iterator d_itr = data_begin; + Iterator np_itr = null_itr; + Iterator nd_itr = null_itr; + + for ( ; ; ) + { + if (p_itr != pattern_end) + { + const type c = *(p_itr); + + if ((data_end != d_itr) && (Compare::cmp(c,*(d_itr)) || (exactly_one == c))) + { + ++d_itr; + ++p_itr; + continue; + } + else if (zero_or_more == c) + { + while ((pattern_end != p_itr) && (zero_or_more == *(p_itr))) + { + ++p_itr; + } + + const type d = *(p_itr); + + while ((data_end != d_itr) && !(Compare::cmp(d,*(d_itr)) || (exactly_one == d))) + { + ++d_itr; + } + + // set backtrack iterators + np_itr = p_itr - 1; + nd_itr = d_itr + 1; + + continue; + } + } + else if (data_end == d_itr) + break; + + if ((data_end == d_itr) || (null_itr == nd_itr)) + return false; + + p_itr = np_itr; + d_itr = nd_itr; + } + + return true; + } + + inline bool wc_match(const std::string& wild_card, + const std::string& str) + { + return match_impl + ( + wild_card.data(), + wild_card.data() + wild_card.size(), + str.data(), + str.data() + str.size(), + '*', '?' + ); + } + + inline bool wc_imatch(const std::string& wild_card, + const std::string& str) + { + return match_impl + ( + wild_card.data(), + wild_card.data() + wild_card.size(), + str.data(), + str.data() + str.size(), + '*', '?' + ); + } + + inline bool sequence_match(const std::string& pattern, + const std::string& str, + std::size_t& diff_index, + char_t& diff_value) + { + if (str.empty()) + { + return ("Z" == pattern); + } + else if ('*' == pattern[0]) + return false; + + typedef std::string::const_iterator itr_t; + + itr_t p_itr = pattern.begin(); + itr_t s_itr = str .begin(); + + const itr_t p_end = pattern.end(); + const itr_t s_end = str .end(); + + while ((s_end != s_itr) && (p_end != p_itr)) + { + if ('*' == (*p_itr)) + { + const char_t target = static_cast(std::toupper(*(p_itr - 1))); + + if ('*' == target) + { + diff_index = static_cast(std::distance(str.begin(),s_itr)); + diff_value = static_cast(std::toupper(*p_itr)); + + return false; + } + else + ++p_itr; + + while (s_itr != s_end) + { + if (target != std::toupper(*s_itr)) + break; + else + ++s_itr; + } + + continue; + } + else if ( + ('?' != *p_itr) && + std::toupper(*p_itr) != std::toupper(*s_itr) + ) + { + diff_index = static_cast(std::distance(str.begin(),s_itr)); + diff_value = static_cast(std::toupper(*p_itr)); + + return false; + } + + ++p_itr; + ++s_itr; + } + + return ( + (s_end == s_itr) && + ( + (p_end == p_itr) || + ('*' == *p_itr) + ) + ); + } + + template + struct set_zero_value_impl + { + static inline void process(T* base_ptr, const std::size_t size) + { + const T zero = T(0); + for (std::size_t i = 0; i < size; ++i) + { + base_ptr[i] = zero; + } + } + }; + + #define pod_set_zero_value(T) \ + template <> \ + struct set_zero_value_impl \ + { \ + static inline void process(T* base_ptr, const std::size_t size) \ + { std::memset(base_ptr, 0x00, size * sizeof(T)); } \ + }; \ + + pod_set_zero_value(float ) + pod_set_zero_value(double ) + pod_set_zero_value(long double) + + #ifdef pod_set_zero_value + #undef pod_set_zero_value + #endif + + template + inline void set_zero_value(T* data, const std::size_t size) + { + set_zero_value_impl::process(data,size); + } + + template + inline void set_zero_value(std::vector& v) + { + set_zero_value(v.data(),v.size()); + } + + static const double pow10[] = + { + 1.0, + 1.0E+001, 1.0E+002, 1.0E+003, 1.0E+004, + 1.0E+005, 1.0E+006, 1.0E+007, 1.0E+008, + 1.0E+009, 1.0E+010, 1.0E+011, 1.0E+012, + 1.0E+013, 1.0E+014, 1.0E+015, 1.0E+016 + }; + + static const std::size_t pow10_size = sizeof(pow10) / sizeof(double); + + namespace numeric + { + namespace constant + { + static const double e = 2.71828182845904523536028747135266249775724709369996; + static const double pi = 3.14159265358979323846264338327950288419716939937510; + static const double pi_2 = 1.57079632679489661923132169163975144209858469968755; + static const double pi_4 = 0.78539816339744830961566084581987572104929234984378; + static const double pi_180 = 0.01745329251994329576923690768488612713442871888542; + static const double _1_pi = 0.31830988618379067153776752674502872406891929148091; + static const double _2_pi = 0.63661977236758134307553505349005744813783858296183; + static const double _180_pi = 57.29577951308232087679815481410517033240547246656443; + static const double log2 = 0.69314718055994530941723212145817656807550013436026; + static const double sqrt2 = 1.41421356237309504880168872420969807856967187537695; + } + + namespace details + { + struct unknown_type_tag { unknown_type_tag() {} }; + struct real_type_tag { real_type_tag () {} }; + struct int_type_tag { int_type_tag () {} }; + + template + struct number_type + { + typedef unknown_type_tag type; + number_type() {} + }; + + #define exprtk_register_real_type_tag(T) \ + template <> struct number_type \ + { typedef real_type_tag type; number_type() {} }; \ + + #define exprtk_register_int_type_tag(T) \ + template <> struct number_type \ + { typedef int_type_tag type; number_type() {} }; \ + + exprtk_register_real_type_tag(float ) + exprtk_register_real_type_tag(double ) + exprtk_register_real_type_tag(long double) + + exprtk_register_int_type_tag(short ) + exprtk_register_int_type_tag(int ) + exprtk_register_int_type_tag(_int64_t ) + exprtk_register_int_type_tag(unsigned short) + exprtk_register_int_type_tag(unsigned int ) + exprtk_register_int_type_tag(_uint64_t ) + + #undef exprtk_register_real_type_tag + #undef exprtk_register_int_type_tag + + template + struct epsilon_type {}; + + #define exprtk_define_epsilon_type(Type, Epsilon) \ + template <> struct epsilon_type \ + { \ + static inline Type value() \ + { \ + const Type epsilon = static_cast(Epsilon); \ + return epsilon; \ + } \ + }; \ + + exprtk_define_epsilon_type(float , 0.00000100000f) + exprtk_define_epsilon_type(double , 0.000000000100) + exprtk_define_epsilon_type(long double, 0.000000000001) + + #undef exprtk_define_epsilon_type + + template + inline bool is_nan_impl(const T v, real_type_tag) + { + return std::not_equal_to()(v,v); + } + + template + inline int to_int32_impl(const T v, real_type_tag) + { + return static_cast(v); + } + + template + inline _int64_t to_int64_impl(const T v, real_type_tag) + { + return static_cast<_int64_t>(v); + } + + template + inline _uint64_t to_uint64_impl(const T v, real_type_tag) + { + return static_cast<_uint64_t>(v); + } + + template + inline bool is_true_impl(const T v) + { + return std::not_equal_to()(T(0),v); + } + + template + inline bool is_false_impl(const T v) + { + return std::equal_to()(T(0),v); + } + + template + inline T abs_impl(const T v, real_type_tag) + { + return ((v < T(0)) ? -v : v); + } + + template + inline T min_impl(const T v0, const T v1, real_type_tag) + { + return std::min(v0,v1); + } + + template + inline T max_impl(const T v0, const T v1, real_type_tag) + { + return std::max(v0,v1); + } + + template + inline T equal_impl(const T v0, const T v1, real_type_tag) + { + const T epsilon = epsilon_type::value(); + return (abs_impl(v0 - v1,real_type_tag()) <= (std::max(T(1),std::max(abs_impl(v0,real_type_tag()),abs_impl(v1,real_type_tag()))) * epsilon)) ? T(1) : T(0); + } + + inline float equal_impl(const float v0, const float v1, real_type_tag) + { + const float epsilon = epsilon_type::value(); + return (abs_impl(v0 - v1,real_type_tag()) <= (std::max(1.0f,std::max(abs_impl(v0,real_type_tag()),abs_impl(v1,real_type_tag()))) * epsilon)) ? 1.0f : 0.0f; + } + + template + inline T equal_impl(const T v0, const T v1, int_type_tag) + { + return (v0 == v1) ? 1 : 0; + } + + template + inline T nequal_impl(const T v0, const T v1, real_type_tag) + { + typedef real_type_tag rtg; + const T epsilon = epsilon_type::value(); + return (abs_impl(v0 - v1,rtg()) > (std::max(T(1),std::max(abs_impl(v0,rtg()),abs_impl(v1,rtg()))) * epsilon)) ? T(1) : T(0); + } + + inline float nequal_impl(const float v0, const float v1, real_type_tag) + { + typedef real_type_tag rtg; + const float epsilon = epsilon_type::value(); + return (abs_impl(v0 - v1,rtg()) > (std::max(1.0f,std::max(abs_impl(v0,rtg()),abs_impl(v1,rtg()))) * epsilon)) ? 1.0f : 0.0f; + } + + template + inline T nequal_impl(const T v0, const T v1, int_type_tag) + { + return (v0 != v1) ? 1 : 0; + } + + template + inline T modulus_impl(const T v0, const T v1, real_type_tag) + { + return std::fmod(v0,v1); + } + + template + inline T modulus_impl(const T v0, const T v1, int_type_tag) + { + return v0 % v1; + } + + template + inline T pow_impl(const T v0, const T v1, real_type_tag) + { + return std::pow(v0,v1); + } + + template + inline T pow_impl(const T v0, const T v1, int_type_tag) + { + return std::pow(static_cast(v0),static_cast(v1)); + } + + template + inline T logn_impl(const T v0, const T v1, real_type_tag) + { + return std::log(v0) / std::log(v1); + } + + template + inline T logn_impl(const T v0, const T v1, int_type_tag) + { + return static_cast(logn_impl(static_cast(v0),static_cast(v1),real_type_tag())); + } + + template + inline T root_impl(const T v0, const T v1, real_type_tag) + { + if (v0 < T(0)) + { + return (v1 == trunc_impl(v1, real_type_tag())) && + (modulus_impl(v1, T(2), real_type_tag()) != T(0)) ? + -std::pow(abs_impl(v0, real_type_tag()), T(1) / v1) : + std::numeric_limits::quiet_NaN(); + } + + return std::pow(v0, T(1) / v1); + } + + template + inline T root_impl(const T v0, const T v1, int_type_tag) + { + return root_impl(static_cast(v0),static_cast(v1),real_type_tag()); + } + + template + inline T round_impl(const T v, real_type_tag) + { + return ((v < T(0)) ? std::ceil(v - T(0.5)) : std::floor(v + T(0.5))); + } + + template + inline T roundn_impl(const T v0, const T v1, real_type_tag) + { + const int index = std::max(0, std::min(pow10_size - 1, static_cast(std::floor(v1)))); + const T p10 = T(pow10[index]); + + if (v0 < T(0)) + return T(std::ceil ((v0 * p10) - T(0.5)) / p10); + else + return T(std::floor((v0 * p10) + T(0.5)) / p10); + } + + template + inline T roundn_impl(const T v0, const T, int_type_tag) + { + return v0; + } + + template + inline T hypot_impl(const T v0, const T v1, real_type_tag) + { + return std::sqrt((v0 * v0) + (v1 * v1)); + } + + template + inline T hypot_impl(const T v0, const T v1, int_type_tag) + { + return static_cast(std::sqrt(static_cast((v0 * v0) + (v1 * v1)))); + } + + template + inline T atan2_impl(const T v0, const T v1, real_type_tag) + { + return std::atan2(v0,v1); + } + + template + inline T atan2_impl(const T, const T, int_type_tag) + { + return 0; + } + + template + inline T shr_impl(const T v0, const T v1, real_type_tag) + { + return v0 * (T(1) / std::pow(T(2),static_cast(static_cast(v1)))); + } + + template + inline T shr_impl(const T v0, const T v1, int_type_tag) + { + return v0 >> v1; + } + + template + inline T shl_impl(const T v0, const T v1, real_type_tag) + { + return v0 * std::pow(T(2),static_cast(static_cast(v1))); + } + + template + inline T shl_impl(const T v0, const T v1, int_type_tag) + { + return v0 << v1; + } + + template + inline T sgn_impl(const T v, real_type_tag) + { + if (v > T(0)) return T(+1); + else if (v < T(0)) return T(-1); + else return T( 0); + } + + template + inline T sgn_impl(const T v, int_type_tag) + { + if (v > T(0)) return T(+1); + else if (v < T(0)) return T(-1); + else return T( 0); + } + + template + inline T and_impl(const T v0, const T v1, real_type_tag) + { + return (is_true_impl(v0) && is_true_impl(v1)) ? T(1) : T(0); + } + + template + inline T and_impl(const T v0, const T v1, int_type_tag) + { + return v0 && v1; + } + + template + inline T nand_impl(const T v0, const T v1, real_type_tag) + { + return (is_false_impl(v0) || is_false_impl(v1)) ? T(1) : T(0); + } + + template + inline T nand_impl(const T v0, const T v1, int_type_tag) + { + return !(v0 && v1); + } + + template + inline T or_impl(const T v0, const T v1, real_type_tag) + { + return (is_true_impl(v0) || is_true_impl(v1)) ? T(1) : T(0); + } + + template + inline T or_impl(const T v0, const T v1, int_type_tag) + { + return (v0 || v1); + } + + template + inline T nor_impl(const T v0, const T v1, real_type_tag) + { + return (is_false_impl(v0) && is_false_impl(v1)) ? T(1) : T(0); + } + + template + inline T nor_impl(const T v0, const T v1, int_type_tag) + { + return !(v0 || v1); + } + + template + inline T xor_impl(const T v0, const T v1, real_type_tag) + { + return (is_false_impl(v0) != is_false_impl(v1)) ? T(1) : T(0); + } + + template + inline T xor_impl(const T v0, const T v1, int_type_tag) + { + return v0 ^ v1; + } + + template + inline T xnor_impl(const T v0, const T v1, real_type_tag) + { + const bool v0_true = is_true_impl(v0); + const bool v1_true = is_true_impl(v1); + + if ((v0_true && v1_true) || (!v0_true && !v1_true)) + return T(1); + else + return T(0); + } + + template + inline T xnor_impl(const T v0, const T v1, int_type_tag) + { + const bool v0_true = is_true_impl(v0); + const bool v1_true = is_true_impl(v1); + + if ((v0_true && v1_true) || (!v0_true && !v1_true)) + return T(1); + else + return T(0); + } + + #if (defined(_MSC_VER) && (_MSC_VER >= 1900)) || !defined(_MSC_VER) + #define exprtk_define_erf(TT, impl) \ + inline TT erf_impl(const TT v) { return impl(v); } \ + + exprtk_define_erf(float , ::erff) + exprtk_define_erf(double , ::erf ) + exprtk_define_erf(long double, ::erfl) + #undef exprtk_define_erf + #endif + + template + inline T erf_impl(const T v, real_type_tag) + { + #if defined(_MSC_VER) && (_MSC_VER < 1900) + // Credits: Abramowitz & Stegun Equations 7.1.25-28 + static const T c[] = + { + T( 1.26551223), T(1.00002368), + T( 0.37409196), T(0.09678418), + T(-0.18628806), T(0.27886807), + T(-1.13520398), T(1.48851587), + T(-0.82215223), T(0.17087277) + }; + + const T t = T(1) / (T(1) + T(0.5) * abs_impl(v,real_type_tag())); + + const T result = T(1) - t * std::exp((-v * v) - + c[0] + t * (c[1] + t * + (c[2] + t * (c[3] + t * + (c[4] + t * (c[5] + t * + (c[6] + t * (c[7] + t * + (c[8] + t * (c[9])))))))))); + + return (v >= T(0)) ? result : -result; + #else + return erf_impl(v); + #endif + } + + template + inline T erf_impl(const T v, int_type_tag) + { + return erf_impl(static_cast(v),real_type_tag()); + } + + #if (defined(_MSC_VER) && (_MSC_VER >= 1900)) || !defined(_MSC_VER) + #define exprtk_define_erfc(TT, impl) \ + inline TT erfc_impl(const TT v) { return impl(v); } \ + + exprtk_define_erfc(float ,::erfcf) + exprtk_define_erfc(double ,::erfc ) + exprtk_define_erfc(long double,::erfcl) + #undef exprtk_define_erfc + #endif + + template + inline T erfc_impl(const T v, real_type_tag) + { + #if defined(_MSC_VER) && (_MSC_VER < 1900) + return T(1) - erf_impl(v,real_type_tag()); + #else + return erfc_impl(v); + #endif + } + + template + inline T erfc_impl(const T v, int_type_tag) + { + return erfc_impl(static_cast(v),real_type_tag()); + } + + template + inline T ncdf_impl(const T v, real_type_tag) + { + return T(0.5) * erfc_impl(-(v / T(numeric::constant::sqrt2)),real_type_tag()); + } + + template + inline T ncdf_impl(const T v, int_type_tag) + { + return ncdf_impl(static_cast(v),real_type_tag()); + } + + template + inline T sinc_impl(const T v, real_type_tag) + { + if (std::abs(v) >= std::numeric_limits::epsilon()) + return(std::sin(v) / v); + else + return T(1); + } + + template + inline T sinc_impl(const T v, int_type_tag) + { + return sinc_impl(static_cast(v),real_type_tag()); + } + + #if __cplusplus >= 201103L + template + inline T acosh_impl(const T v, real_type_tag) + { + return std::acosh(v); + } + + template + inline T asinh_impl(const T v, real_type_tag) + { + return std::asinh(v); + } + + template + inline T atanh_impl(const T v, real_type_tag) + { + return std::atanh(v); + } + + template + inline T trunc_impl(const T v, real_type_tag) + { + return std::trunc(v); + } + + template + inline T expm1_impl(const T v, real_type_tag) + { + return std::expm1(v); + } + + template + inline T expm1_impl(const T v, int_type_tag) + { + return std::expm1(v); + } + + template + inline T log1p_impl(const T v, real_type_tag) + { + return std::log1p(v); + } + + template + inline T log1p_impl(const T v, int_type_tag) + { + return std::log1p(v); + } + #else + template + inline T acosh_impl(const T v, real_type_tag) + { + return std::log(v + std::sqrt((v * v) - T(1))); + } + + template + inline T asinh_impl(const T v, real_type_tag) + { + return std::log(v + std::sqrt((v * v) + T(1))); + } + + template + inline T atanh_impl(const T v, real_type_tag) + { + return (std::log(T(1) + v) - std::log(T(1) - v)) / T(2); + } + + template + inline T trunc_impl(const T v, real_type_tag) + { + return T(static_cast(v)); + } + + template + inline T expm1_impl(const T v, real_type_tag) + { + if (abs_impl(v,real_type_tag()) < T(0.00001)) + return v + (T(0.5) * v * v); + else + return std::exp(v) - T(1); + } + + template + inline T expm1_impl(const T v, int_type_tag) + { + return T(std::exp(v)) - T(1); + } + + template + inline T log1p_impl(const T v, real_type_tag) + { + if (v > T(-1)) + { + if (abs_impl(v,real_type_tag()) > T(0.0001)) + { + return std::log(T(1) + v); + } + else + return (T(-0.5) * v + T(1)) * v; + } + + return std::numeric_limits::quiet_NaN(); + } + + template + inline T log1p_impl(const T v, int_type_tag) + { + if (v > T(-1)) + { + return std::log(T(1) + v); + } + + return std::numeric_limits::quiet_NaN(); + } + #endif + + template inline T acos_impl(const T v, real_type_tag) { return std::acos (v); } + template inline T asin_impl(const T v, real_type_tag) { return std::asin (v); } + template inline T atan_impl(const T v, real_type_tag) { return std::atan (v); } + template inline T ceil_impl(const T v, real_type_tag) { return std::ceil (v); } + template inline T cos_impl(const T v, real_type_tag) { return std::cos (v); } + template inline T cosh_impl(const T v, real_type_tag) { return std::cosh (v); } + template inline T exp_impl(const T v, real_type_tag) { return std::exp (v); } + template inline T floor_impl(const T v, real_type_tag) { return std::floor(v); } + template inline T log_impl(const T v, real_type_tag) { return std::log (v); } + template inline T log10_impl(const T v, real_type_tag) { return std::log10(v); } + template inline T log2_impl(const T v, real_type_tag) { return std::log(v)/T(numeric::constant::log2); } + template inline T neg_impl(const T v, real_type_tag) { return -v; } + template inline T pos_impl(const T v, real_type_tag) { return +v; } + template inline T sin_impl(const T v, real_type_tag) { return std::sin (v); } + template inline T sinh_impl(const T v, real_type_tag) { return std::sinh (v); } + template inline T sqrt_impl(const T v, real_type_tag) { return std::sqrt (v); } + template inline T tan_impl(const T v, real_type_tag) { return std::tan (v); } + template inline T tanh_impl(const T v, real_type_tag) { return std::tanh (v); } + template inline T cot_impl(const T v, real_type_tag) { return T(1) / std::tan(v); } + template inline T sec_impl(const T v, real_type_tag) { return T(1) / std::cos(v); } + template inline T csc_impl(const T v, real_type_tag) { return T(1) / std::sin(v); } + template inline T r2d_impl(const T v, real_type_tag) { return (v * T(numeric::constant::_180_pi)); } + template inline T d2r_impl(const T v, real_type_tag) { return (v * T(numeric::constant::pi_180)); } + template inline T d2g_impl(const T v, real_type_tag) { return (v * T(10.0/9.0)); } + template inline T g2d_impl(const T v, real_type_tag) { return (v * T(9.0/10.0)); } + template inline T notl_impl(const T v, real_type_tag) { return (std::not_equal_to()(T(0),v) ? T(0) : T(1)); } + template inline T frac_impl(const T v, real_type_tag) { return (v - trunc_impl(v,real_type_tag())); } + + template inline T const_pi_impl(real_type_tag) { return T(numeric::constant::pi); } + template inline T const_e_impl(real_type_tag) { return T(numeric::constant::e); } + template inline T const_qnan_impl(real_type_tag) { return std::numeric_limits::quiet_NaN(); } + + template inline T abs_impl(const T v, int_type_tag) { return ((v >= T(0)) ? v : -v); } + template inline T exp_impl(const T v, int_type_tag) { return std::exp (v); } + template inline T log_impl(const T v, int_type_tag) { return std::log (v); } + template inline T log10_impl(const T v, int_type_tag) { return std::log10(v); } + template inline T log2_impl(const T v, int_type_tag) { return std::log(v)/T(numeric::constant::log2); } + template inline T neg_impl(const T v, int_type_tag) { return -v; } + template inline T pos_impl(const T v, int_type_tag) { return +v; } + template inline T ceil_impl(const T v, int_type_tag) { return v; } + template inline T floor_impl(const T v, int_type_tag) { return v; } + template inline T round_impl(const T v, int_type_tag) { return v; } + template inline T notl_impl(const T v, int_type_tag) { return !v; } + template inline T sqrt_impl(const T v, int_type_tag) { return std::sqrt (v); } + template inline T frac_impl(const T , int_type_tag) { return T(0); } + template inline T trunc_impl(const T v, int_type_tag) { return v; } + template inline T acos_impl(const T , int_type_tag) { return std::numeric_limits::quiet_NaN(); } + template inline T acosh_impl(const T , int_type_tag) { return std::numeric_limits::quiet_NaN(); } + template inline T asin_impl(const T , int_type_tag) { return std::numeric_limits::quiet_NaN(); } + template inline T asinh_impl(const T , int_type_tag) { return std::numeric_limits::quiet_NaN(); } + template inline T atan_impl(const T , int_type_tag) { return std::numeric_limits::quiet_NaN(); } + template inline T atanh_impl(const T , int_type_tag) { return std::numeric_limits::quiet_NaN(); } + template inline T cos_impl(const T , int_type_tag) { return std::numeric_limits::quiet_NaN(); } + template inline T cosh_impl(const T , int_type_tag) { return std::numeric_limits::quiet_NaN(); } + template inline T sin_impl(const T , int_type_tag) { return std::numeric_limits::quiet_NaN(); } + template inline T sinh_impl(const T , int_type_tag) { return std::numeric_limits::quiet_NaN(); } + template inline T tan_impl(const T , int_type_tag) { return std::numeric_limits::quiet_NaN(); } + template inline T tanh_impl(const T , int_type_tag) { return std::numeric_limits::quiet_NaN(); } + template inline T cot_impl(const T , int_type_tag) { return std::numeric_limits::quiet_NaN(); } + template inline T sec_impl(const T , int_type_tag) { return std::numeric_limits::quiet_NaN(); } + template inline T csc_impl(const T , int_type_tag) { return std::numeric_limits::quiet_NaN(); } + + template + inline bool is_integer_impl(const T& v, real_type_tag) + { + return std::equal_to()(T(0),std::fmod(v,T(1))); + } + + template + inline bool is_integer_impl(const T&, int_type_tag) + { + return true; + } + } + + template + struct numeric_info { enum { length = 0, size = 32, bound_length = 0, min_exp = 0, max_exp = 0 }; }; + + template <> struct numeric_info { enum { length = 10, size = 16, bound_length = 9 }; }; + template <> struct numeric_info { enum { min_exp = -38, max_exp = +38 }; }; + template <> struct numeric_info { enum { min_exp = -308, max_exp = +308 }; }; + template <> struct numeric_info { enum { min_exp = -308, max_exp = +308 }; }; + + template + inline int to_int32(const T v) + { + const typename details::number_type::type num_type; + return to_int32_impl(v, num_type); + } + + template + inline _int64_t to_int64(const T v) + { + const typename details::number_type::type num_type; + return to_int64_impl(v, num_type); + } + + template + inline _uint64_t to_uint64(const T v) + { + const typename details::number_type::type num_type; + return to_uint64_impl(v, num_type); + } + + template + inline bool is_nan(const T v) + { + const typename details::number_type::type num_type; + return is_nan_impl(v, num_type); + } + + template + inline T min(const T v0, const T v1) + { + const typename details::number_type::type num_type; + return min_impl(v0, v1, num_type); + } + + template + inline T max(const T v0, const T v1) + { + const typename details::number_type::type num_type; + return max_impl(v0, v1, num_type); + } + + template + inline T equal(const T v0, const T v1) + { + const typename details::number_type::type num_type; + return equal_impl(v0, v1, num_type); + } + + template + inline T nequal(const T v0, const T v1) + { + const typename details::number_type::type num_type; + return nequal_impl(v0, v1, num_type); + } + + template + inline T modulus(const T v0, const T v1) + { + const typename details::number_type::type num_type; + return modulus_impl(v0, v1, num_type); + } + + template + inline T pow(const T v0, const T v1) + { + const typename details::number_type::type num_type; + return pow_impl(v0, v1, num_type); + } + + template + inline T logn(const T v0, const T v1) + { + const typename details::number_type::type num_type; + return logn_impl(v0, v1, num_type); + } + + template + inline T root(const T v0, const T v1) + { + const typename details::number_type::type num_type; + return root_impl(v0, v1, num_type); + } + + template + inline T roundn(const T v0, const T v1) + { + const typename details::number_type::type num_type; + return roundn_impl(v0, v1, num_type); + } + + template + inline T hypot(const T v0, const T v1) + { + const typename details::number_type::type num_type; + return hypot_impl(v0, v1, num_type); + } + + template + inline T atan2(const T v0, const T v1) + { + const typename details::number_type::type num_type; + return atan2_impl(v0, v1, num_type); + } + + template + inline T shr(const T v0, const T v1) + { + const typename details::number_type::type num_type; + return shr_impl(v0, v1, num_type); + } + + template + inline T shl(const T v0, const T v1) + { + const typename details::number_type::type num_type; + return shl_impl(v0, v1, num_type); + } + + template + inline T and_opr(const T v0, const T v1) + { + const typename details::number_type::type num_type; + return and_impl(v0, v1, num_type); + } + + template + inline T nand_opr(const T v0, const T v1) + { + const typename details::number_type::type num_type; + return nand_impl(v0, v1, num_type); + } + + template + inline T or_opr(const T v0, const T v1) + { + const typename details::number_type::type num_type; + return or_impl(v0, v1, num_type); + } + + template + inline T nor_opr(const T v0, const T v1) + { + const typename details::number_type::type num_type; + return nor_impl(v0, v1, num_type); + } + + template + inline T xor_opr(const T v0, const T v1) + { + const typename details::number_type::type num_type; + return xor_impl(v0, v1, num_type); + } + + template + inline T xnor_opr(const T v0, const T v1) + { + const typename details::number_type::type num_type; + return xnor_impl(v0, v1, num_type); + } + + template + inline bool is_integer(const T v) + { + const typename details::number_type::type num_type; + return is_integer_impl(v, num_type); + } + + template + struct fast_exp + { + static inline T result(T v) + { + unsigned int k = N; + T l = T(1); + + while (k) + { + if (1 == (k % 2)) + { + l *= v; + --k; + } + + v *= v; + k /= 2; + } + + return l; + } + }; + + template struct fast_exp { static inline T result(const T v) { T v_5 = fast_exp::result(v); return v_5 * v_5; } }; + template struct fast_exp { static inline T result(const T v) { return fast_exp::result(v) * v; } }; + template struct fast_exp { static inline T result(const T v) { T v_4 = fast_exp::result(v); return v_4 * v_4; } }; + template struct fast_exp { static inline T result(const T v) { return fast_exp::result(v) * v; } }; + template struct fast_exp { static inline T result(const T v) { T v_3 = fast_exp::result(v); return v_3 * v_3; } }; + template struct fast_exp { static inline T result(const T v) { return fast_exp::result(v) * v; } }; + template struct fast_exp { static inline T result(const T v) { T v_2 = v * v; return v_2 * v_2; } }; + template struct fast_exp { static inline T result(const T v) { return v * v * v; } }; + template struct fast_exp { static inline T result(const T v) { return v * v; } }; + template struct fast_exp { static inline T result(const T v) { return v; } }; + template struct fast_exp { static inline T result(const T ) { return T(1); } }; + + #define exprtk_define_unary_function(FunctionName) \ + template \ + inline T FunctionName (const T v) \ + { \ + const typename details::number_type::type num_type; \ + return FunctionName##_impl(v,num_type); \ + } \ + + exprtk_define_unary_function(abs ) + exprtk_define_unary_function(acos ) + exprtk_define_unary_function(acosh) + exprtk_define_unary_function(asin ) + exprtk_define_unary_function(asinh) + exprtk_define_unary_function(atan ) + exprtk_define_unary_function(atanh) + exprtk_define_unary_function(ceil ) + exprtk_define_unary_function(cos ) + exprtk_define_unary_function(cosh ) + exprtk_define_unary_function(exp ) + exprtk_define_unary_function(expm1) + exprtk_define_unary_function(floor) + exprtk_define_unary_function(log ) + exprtk_define_unary_function(log10) + exprtk_define_unary_function(log2 ) + exprtk_define_unary_function(log1p) + exprtk_define_unary_function(neg ) + exprtk_define_unary_function(pos ) + exprtk_define_unary_function(round) + exprtk_define_unary_function(sin ) + exprtk_define_unary_function(sinc ) + exprtk_define_unary_function(sinh ) + exprtk_define_unary_function(sqrt ) + exprtk_define_unary_function(tan ) + exprtk_define_unary_function(tanh ) + exprtk_define_unary_function(cot ) + exprtk_define_unary_function(sec ) + exprtk_define_unary_function(csc ) + exprtk_define_unary_function(r2d ) + exprtk_define_unary_function(d2r ) + exprtk_define_unary_function(d2g ) + exprtk_define_unary_function(g2d ) + exprtk_define_unary_function(notl ) + exprtk_define_unary_function(sgn ) + exprtk_define_unary_function(erf ) + exprtk_define_unary_function(erfc ) + exprtk_define_unary_function(ncdf ) + exprtk_define_unary_function(frac ) + exprtk_define_unary_function(trunc) + #undef exprtk_define_unary_function + } + + template + inline T compute_pow10(T d, const int exponent) + { + static const double fract10[] = + { + 0.0, + 1.0E+001, 1.0E+002, 1.0E+003, 1.0E+004, 1.0E+005, 1.0E+006, 1.0E+007, 1.0E+008, 1.0E+009, 1.0E+010, + 1.0E+011, 1.0E+012, 1.0E+013, 1.0E+014, 1.0E+015, 1.0E+016, 1.0E+017, 1.0E+018, 1.0E+019, 1.0E+020, + 1.0E+021, 1.0E+022, 1.0E+023, 1.0E+024, 1.0E+025, 1.0E+026, 1.0E+027, 1.0E+028, 1.0E+029, 1.0E+030, + 1.0E+031, 1.0E+032, 1.0E+033, 1.0E+034, 1.0E+035, 1.0E+036, 1.0E+037, 1.0E+038, 1.0E+039, 1.0E+040, + 1.0E+041, 1.0E+042, 1.0E+043, 1.0E+044, 1.0E+045, 1.0E+046, 1.0E+047, 1.0E+048, 1.0E+049, 1.0E+050, + 1.0E+051, 1.0E+052, 1.0E+053, 1.0E+054, 1.0E+055, 1.0E+056, 1.0E+057, 1.0E+058, 1.0E+059, 1.0E+060, + 1.0E+061, 1.0E+062, 1.0E+063, 1.0E+064, 1.0E+065, 1.0E+066, 1.0E+067, 1.0E+068, 1.0E+069, 1.0E+070, + 1.0E+071, 1.0E+072, 1.0E+073, 1.0E+074, 1.0E+075, 1.0E+076, 1.0E+077, 1.0E+078, 1.0E+079, 1.0E+080, + 1.0E+081, 1.0E+082, 1.0E+083, 1.0E+084, 1.0E+085, 1.0E+086, 1.0E+087, 1.0E+088, 1.0E+089, 1.0E+090, + 1.0E+091, 1.0E+092, 1.0E+093, 1.0E+094, 1.0E+095, 1.0E+096, 1.0E+097, 1.0E+098, 1.0E+099, 1.0E+100, + 1.0E+101, 1.0E+102, 1.0E+103, 1.0E+104, 1.0E+105, 1.0E+106, 1.0E+107, 1.0E+108, 1.0E+109, 1.0E+110, + 1.0E+111, 1.0E+112, 1.0E+113, 1.0E+114, 1.0E+115, 1.0E+116, 1.0E+117, 1.0E+118, 1.0E+119, 1.0E+120, + 1.0E+121, 1.0E+122, 1.0E+123, 1.0E+124, 1.0E+125, 1.0E+126, 1.0E+127, 1.0E+128, 1.0E+129, 1.0E+130, + 1.0E+131, 1.0E+132, 1.0E+133, 1.0E+134, 1.0E+135, 1.0E+136, 1.0E+137, 1.0E+138, 1.0E+139, 1.0E+140, + 1.0E+141, 1.0E+142, 1.0E+143, 1.0E+144, 1.0E+145, 1.0E+146, 1.0E+147, 1.0E+148, 1.0E+149, 1.0E+150, + 1.0E+151, 1.0E+152, 1.0E+153, 1.0E+154, 1.0E+155, 1.0E+156, 1.0E+157, 1.0E+158, 1.0E+159, 1.0E+160, + 1.0E+161, 1.0E+162, 1.0E+163, 1.0E+164, 1.0E+165, 1.0E+166, 1.0E+167, 1.0E+168, 1.0E+169, 1.0E+170, + 1.0E+171, 1.0E+172, 1.0E+173, 1.0E+174, 1.0E+175, 1.0E+176, 1.0E+177, 1.0E+178, 1.0E+179, 1.0E+180, + 1.0E+181, 1.0E+182, 1.0E+183, 1.0E+184, 1.0E+185, 1.0E+186, 1.0E+187, 1.0E+188, 1.0E+189, 1.0E+190, + 1.0E+191, 1.0E+192, 1.0E+193, 1.0E+194, 1.0E+195, 1.0E+196, 1.0E+197, 1.0E+198, 1.0E+199, 1.0E+200, + 1.0E+201, 1.0E+202, 1.0E+203, 1.0E+204, 1.0E+205, 1.0E+206, 1.0E+207, 1.0E+208, 1.0E+209, 1.0E+210, + 1.0E+211, 1.0E+212, 1.0E+213, 1.0E+214, 1.0E+215, 1.0E+216, 1.0E+217, 1.0E+218, 1.0E+219, 1.0E+220, + 1.0E+221, 1.0E+222, 1.0E+223, 1.0E+224, 1.0E+225, 1.0E+226, 1.0E+227, 1.0E+228, 1.0E+229, 1.0E+230, + 1.0E+231, 1.0E+232, 1.0E+233, 1.0E+234, 1.0E+235, 1.0E+236, 1.0E+237, 1.0E+238, 1.0E+239, 1.0E+240, + 1.0E+241, 1.0E+242, 1.0E+243, 1.0E+244, 1.0E+245, 1.0E+246, 1.0E+247, 1.0E+248, 1.0E+249, 1.0E+250, + 1.0E+251, 1.0E+252, 1.0E+253, 1.0E+254, 1.0E+255, 1.0E+256, 1.0E+257, 1.0E+258, 1.0E+259, 1.0E+260, + 1.0E+261, 1.0E+262, 1.0E+263, 1.0E+264, 1.0E+265, 1.0E+266, 1.0E+267, 1.0E+268, 1.0E+269, 1.0E+270, + 1.0E+271, 1.0E+272, 1.0E+273, 1.0E+274, 1.0E+275, 1.0E+276, 1.0E+277, 1.0E+278, 1.0E+279, 1.0E+280, + 1.0E+281, 1.0E+282, 1.0E+283, 1.0E+284, 1.0E+285, 1.0E+286, 1.0E+287, 1.0E+288, 1.0E+289, 1.0E+290, + 1.0E+291, 1.0E+292, 1.0E+293, 1.0E+294, 1.0E+295, 1.0E+296, 1.0E+297, 1.0E+298, 1.0E+299, 1.0E+300, + 1.0E+301, 1.0E+302, 1.0E+303, 1.0E+304, 1.0E+305, 1.0E+306, 1.0E+307, 1.0E+308 + }; + + static const int fract10_size = static_cast(sizeof(fract10) / sizeof(double)); + + const int e = std::abs(exponent); + + if (exponent >= std::numeric_limits::min_exponent10) + { + if (e < fract10_size) + { + if (exponent > 0) + return T(d * fract10[e]); + else + return T(d / fract10[e]); + } + else + return T(d * std::pow(10.0, 10.0 * exponent)); + } + else + { + d /= T(fract10[ -std::numeric_limits::min_exponent10]); + return T(d / fract10[-exponent + std::numeric_limits::min_exponent10]); + } + } + + template + inline bool string_to_type_converter_impl_ref(Iterator& itr, const Iterator end, T& result) + { + if (itr == end) + return false; + + const bool negative = ('-' == (*itr)); + + if (negative || ('+' == (*itr))) + { + if (end == ++itr) + return false; + } + + static const uchar_t zero = static_cast('0'); + + while ((end != itr) && (zero == (*itr))) ++itr; + + bool return_result = true; + unsigned int digit = 0; + const std::size_t length = static_cast(std::distance(itr,end)); + + if (length <= 4) + { + switch (length) + { + #ifdef exprtk_use_lut + + #define exprtk_process_digit \ + if ((digit = details::digit_table[(int)*itr++]) < 10) \ + result = result * 10 + (digit); \ + else \ + { \ + return_result = false; \ + break; \ + } \ + exprtk_fallthrough \ + + #else + + #define exprtk_process_digit \ + if ((digit = (*itr++ - zero)) < 10) \ + result = result * T(10) + digit; \ + else \ + { \ + return_result = false; \ + break; \ + } \ + exprtk_fallthrough \ + + #endif + + case 4 : exprtk_process_digit + case 3 : exprtk_process_digit + case 2 : exprtk_process_digit + case 1 : if ((digit = (*itr - zero))>= 10) + { + digit = 0; + return_result = false; + } + + #undef exprtk_process_digit + } + } + else + return_result = false; + + if (length && return_result) + { + result = result * 10 + static_cast(digit); + ++itr; + } + + result = negative ? -result : result; + return return_result; + } + + template + static inline bool parse_nan(Iterator& itr, const Iterator end, T& t) + { + typedef typename std::iterator_traits::value_type type; + + static const std::size_t nan_length = 3; + + if (std::distance(itr,end) != static_cast(nan_length)) + return false; + + if (static_cast('n') == (*itr)) + { + if ( + (static_cast('a') != *(itr + 1)) || + (static_cast('n') != *(itr + 2)) + ) + { + return false; + } + } + else if ( + (static_cast('A') != *(itr + 1)) || + (static_cast('N') != *(itr + 2)) + ) + { + return false; + } + + t = std::numeric_limits::quiet_NaN(); + + return true; + } + + template + static inline bool parse_inf(Iterator& itr, const Iterator end, T& t, const bool negative) + { + static const char_t inf_uc[] = "INFINITY"; + static const char_t inf_lc[] = "infinity"; + static const std::size_t inf_length = 8; + + const std::size_t length = static_cast(std::distance(itr,end)); + + if ((3 != length) && (inf_length != length)) + return false; + + char_cptr inf_itr = ('i' == (*itr)) ? inf_lc : inf_uc; + + while (end != itr) + { + if (*inf_itr == static_cast(*itr)) + { + ++itr; + ++inf_itr; + continue; + } + else + return false; + } + + if (negative) + t = -std::numeric_limits::infinity(); + else + t = std::numeric_limits::infinity(); + + return true; + } + + template + inline bool valid_exponent(const int exponent, numeric::details::real_type_tag) + { + using namespace details::numeric; + return (numeric_info::min_exp <= exponent) && (exponent <= numeric_info::max_exp); + } + + template + inline bool string_to_real(Iterator& itr_external, const Iterator end, T& t, numeric::details::real_type_tag) + { + if (end == itr_external) return false; + + Iterator itr = itr_external; + + T d = T(0); + + const bool negative = ('-' == (*itr)); + + if (negative || '+' == (*itr)) + { + if (end == ++itr) + return false; + } + + bool instate = false; + + static const char_t zero = static_cast('0'); + + #define parse_digit_1(d) \ + if ((digit = (*itr - zero)) < 10) \ + { d = d * T(10) + digit; } \ + else \ + { break; } \ + if (end == ++itr) break; \ + + #define parse_digit_2(d) \ + if ((digit = (*itr - zero)) < 10) \ + { d = d * T(10) + digit; } \ + else \ + { break; } \ + ++itr; \ + + if ('.' != (*itr)) + { + const Iterator curr = itr; + + while ((end != itr) && (zero == (*itr))) ++itr; + + while (end != itr) + { + unsigned int digit; + parse_digit_1(d) + parse_digit_1(d) + parse_digit_2(d) + } + + if (curr != itr) instate = true; + } + + int exponent = 0; + + if (end != itr) + { + if ('.' == (*itr)) + { + const Iterator curr = ++itr; + T tmp_d = T(0); + + while (end != itr) + { + unsigned int digit; + parse_digit_1(tmp_d) + parse_digit_1(tmp_d) + parse_digit_2(tmp_d) + } + + if (curr != itr) + { + instate = true; + + const int frac_exponent = static_cast(-std::distance(curr, itr)); + + if (!valid_exponent(frac_exponent, numeric::details::real_type_tag())) + return false; + + d += compute_pow10(tmp_d, frac_exponent); + } + + #undef parse_digit_1 + #undef parse_digit_2 + } + + if (end != itr) + { + typename std::iterator_traits::value_type c = (*itr); + + if (('e' == c) || ('E' == c)) + { + int exp = 0; + + if (!details::string_to_type_converter_impl_ref(++itr, end, exp)) + { + if (end == itr) + return false; + else + c = (*itr); + } + + exponent += exp; + } + + if (end != itr) + { + if (('f' == c) || ('F' == c) || ('l' == c) || ('L' == c)) + ++itr; + else if ('#' == c) + { + if (end == ++itr) + return false; + else if (('I' <= (*itr)) && ((*itr) <= 'n')) + { + if (('i' == (*itr)) || ('I' == (*itr))) + { + return parse_inf(itr, end, t, negative); + } + else if (('n' == (*itr)) || ('N' == (*itr))) + { + return parse_nan(itr, end, t); + } + else + return false; + } + else + return false; + } + else if (('I' <= (*itr)) && ((*itr) <= 'n')) + { + if (('i' == (*itr)) || ('I' == (*itr))) + { + return parse_inf(itr, end, t, negative); + } + else if (('n' == (*itr)) || ('N' == (*itr))) + { + return parse_nan(itr, end, t); + } + else + return false; + } + else + return false; + } + } + } + + if ((end != itr) || (!instate)) + return false; + else if (!valid_exponent(exponent, numeric::details::real_type_tag())) + return false; + else if (exponent) + d = compute_pow10(d,exponent); + + t = static_cast((negative) ? -d : d); + return true; + } + + template + inline bool string_to_real(const std::string& s, T& t) + { + const typename numeric::details::number_type::type num_type; + + char_cptr begin = s.data(); + char_cptr end = s.data() + s.size(); + + return string_to_real(begin, end, t, num_type); + } + + template + struct functor_t + { + /* + Note: The following definitions for Type, may require tweaking + based on the compiler and target architecture. The benchmark + should provide enough information to make the right choice. + */ + //typedef T Type; + //typedef const T Type; + typedef const T& Type; + typedef T& RefType; + typedef T (*qfunc_t)(Type t0, Type t1, Type t2, Type t3); + typedef T (*tfunc_t)(Type t0, Type t1, Type t2); + typedef T (*bfunc_t)(Type t0, Type t1); + typedef T (*ufunc_t)(Type t0); + }; + + } // namespace details + + struct loop_runtime_check + { + enum loop_types + { + e_invalid = 0, + e_for_loop = 1, + e_while_loop = 2, + e_repeat_until_loop = 4, + e_all_loops = 7 + }; + + enum violation_type + { + e_unknown = 0, + e_iteration_count = 1, + e_timeout = 2 + }; + + loop_types loop_set; + + loop_runtime_check() + : loop_set(e_invalid) + , max_loop_iterations(0) + {} + + details::_uint64_t max_loop_iterations; + + struct violation_context + { + loop_types loop; + violation_type violation; + details::_uint64_t iteration_count; + }; + + virtual bool check() + { + return true; + } + + virtual void handle_runtime_violation(const violation_context&) + { + throw std::runtime_error("ExprTk Loop runtime violation."); + } + + virtual ~loop_runtime_check() + {} + }; + + typedef loop_runtime_check* loop_runtime_check_ptr; + + struct vector_access_runtime_check + { + struct violation_context + { + void* base_ptr; + void* end_ptr; + void* access_ptr; + std::size_t type_size; + }; + + virtual ~vector_access_runtime_check() + {} + + virtual bool handle_runtime_violation(violation_context& /*context*/) + { + throw std::runtime_error("ExprTk runtime vector access violation."); + #if !defined(_MSC_VER) && !defined(__NVCOMPILER) + return false; + #endif + } + }; + + typedef vector_access_runtime_check* vector_access_runtime_check_ptr; + + struct assert_check + { + struct assert_context + { + std::string condition; + std::string message; + std::string id; + std::size_t offet; + }; + + virtual ~assert_check() + {} + + virtual void handle_assert(const assert_context& /*context*/) + {} + }; + + typedef assert_check* assert_check_ptr; + + struct compilation_check + { + struct compilation_context + { + std::string error_message; + }; + + virtual bool continue_compilation(compilation_context& /*context*/) = 0; + + virtual ~compilation_check() + {} + }; + + typedef compilation_check* compilation_check_ptr; + + namespace lexer + { + struct token + { + enum token_type + { + e_none = 0, e_error = 1, e_err_symbol = 2, + e_err_number = 3, e_err_string = 4, e_err_sfunc = 5, + e_eof = 6, e_number = 7, e_symbol = 8, + e_string = 9, e_assign = 10, e_addass = 11, + e_subass = 12, e_mulass = 13, e_divass = 14, + e_modass = 15, e_shr = 16, e_shl = 17, + e_lte = 18, e_ne = 19, e_gte = 20, + e_swap = 21, e_lt = '<', e_gt = '>', + e_eq = '=', e_rbracket = ')', e_lbracket = '(', + e_rsqrbracket = ']', e_lsqrbracket = '[', e_rcrlbracket = '}', + e_lcrlbracket = '{', e_comma = ',', e_add = '+', + e_sub = '-', e_div = '/', e_mul = '*', + e_mod = '%', e_pow = '^', e_colon = ':', + e_ternary = '?' + }; + + token() + : type(e_none) + , value("") + , position(std::numeric_limits::max()) + {} + + void clear() + { + type = e_none; + value = ""; + position = std::numeric_limits::max(); + } + + template + inline token& set_operator(const token_type tt, + const Iterator begin, const Iterator end, + const Iterator base_begin = Iterator(0)) + { + type = tt; + value.assign(begin,end); + if (base_begin) + position = static_cast(std::distance(base_begin,begin)); + return (*this); + } + + template + inline token& set_symbol(const Iterator begin, const Iterator end, const Iterator base_begin = Iterator(0)) + { + type = e_symbol; + value.assign(begin,end); + if (base_begin) + position = static_cast(std::distance(base_begin,begin)); + return (*this); + } + + template + inline token& set_numeric(const Iterator begin, const Iterator end, const Iterator base_begin = Iterator(0)) + { + type = e_number; + value.assign(begin,end); + if (base_begin) + position = static_cast(std::distance(base_begin,begin)); + return (*this); + } + + template + inline token& set_string(const Iterator begin, const Iterator end, const Iterator base_begin = Iterator(0)) + { + type = e_string; + value.assign(begin,end); + if (base_begin) + position = static_cast(std::distance(base_begin,begin)); + return (*this); + } + + inline token& set_string(const std::string& s, const std::size_t p) + { + type = e_string; + value = s; + position = p; + return (*this); + } + + template + inline token& set_error(const token_type et, + const Iterator begin, const Iterator end, + const Iterator base_begin = Iterator(0)) + { + if ( + (e_error == et) || + (e_err_symbol == et) || + (e_err_number == et) || + (e_err_string == et) || + (e_err_sfunc == et) + ) + { + type = et; + } + else + type = e_error; + + value.assign(begin,end); + + if (base_begin) + position = static_cast(std::distance(base_begin,begin)); + + return (*this); + } + + static inline std::string to_str(token_type t) + { + switch (t) + { + case e_none : return "NONE"; + case e_error : return "ERROR"; + case e_err_symbol : return "ERROR_SYMBOL"; + case e_err_number : return "ERROR_NUMBER"; + case e_err_string : return "ERROR_STRING"; + case e_eof : return "EOF"; + case e_number : return "NUMBER"; + case e_symbol : return "SYMBOL"; + case e_string : return "STRING"; + case e_assign : return ":="; + case e_addass : return "+="; + case e_subass : return "-="; + case e_mulass : return "*="; + case e_divass : return "/="; + case e_modass : return "%="; + case e_shr : return ">>"; + case e_shl : return "<<"; + case e_lte : return "<="; + case e_ne : return "!="; + case e_gte : return ">="; + case e_lt : return "<"; + case e_gt : return ">"; + case e_eq : return "="; + case e_rbracket : return ")"; + case e_lbracket : return "("; + case e_rsqrbracket : return "]"; + case e_lsqrbracket : return "["; + case e_rcrlbracket : return "}"; + case e_lcrlbracket : return "{"; + case e_comma : return ","; + case e_add : return "+"; + case e_sub : return "-"; + case e_div : return "/"; + case e_mul : return "*"; + case e_mod : return "%"; + case e_pow : return "^"; + case e_colon : return ":"; + case e_ternary : return "?"; + case e_swap : return "<=>"; + default : return "UNKNOWN"; + } + } + + static inline std::string seperator_to_str(const token_type t) + { + switch (t) + { + case e_comma : return ","; + case e_colon : return ":"; + case e_eof : return ";"; + default : return "UNKNOWN"; + } + + #if !defined(_MSC_VER) && !defined(__NVCOMPILER) + return "UNKNOWN"; + #endif + } + + inline bool is_error() const + { + return (e_error == type) || + (e_err_symbol == type) || + (e_err_number == type) || + (e_err_string == type) || + (e_err_sfunc == type) ; + } + + token_type type; + std::string value; + std::size_t position; + }; + + class generator + { + public: + + typedef token token_t; + typedef std::vector token_list_t; + typedef token_list_t::iterator token_list_itr_t; + typedef details::char_t char_t; + + generator() + : base_itr_(0) + , s_itr_ (0) + , s_end_ (0) + { + clear(); + } + + inline void clear() + { + base_itr_ = 0; + s_itr_ = 0; + s_end_ = 0; + token_list_.clear(); + token_itr_ = token_list_.end(); + store_token_itr_ = token_list_.end(); + } + + inline bool process(const std::string& str) + { + base_itr_ = str.data(); + s_itr_ = str.data(); + s_end_ = str.data() + str.size(); + + eof_token_.set_operator(token_t::e_eof, s_end_, s_end_, base_itr_); + token_list_.clear(); + + while (!is_end(s_itr_)) + { + scan_token(); + + if (!token_list_.empty() && token_list_.back().is_error()) + return false; + } + + return true; + } + + inline bool empty() const + { + return token_list_.empty(); + } + + inline std::size_t size() const + { + return token_list_.size(); + } + + inline void begin() + { + token_itr_ = token_list_.begin(); + store_token_itr_ = token_list_.begin(); + } + + inline void store() + { + store_token_itr_ = token_itr_; + } + + inline void restore() + { + token_itr_ = store_token_itr_; + } + + inline token_t& next_token() + { + if (token_list_.end() != token_itr_) + { + return *token_itr_++; + } + else + return eof_token_; + } + + inline token_t& peek_next_token() + { + if (token_list_.end() != token_itr_) + { + return *token_itr_; + } + else + return eof_token_; + } + + inline token_t& operator[](const std::size_t& index) + { + if (index < token_list_.size()) + { + return token_list_[index]; + } + else + return eof_token_; + } + + inline token_t operator[](const std::size_t& index) const + { + if (index < token_list_.size()) + { + return token_list_[index]; + } + else + return eof_token_; + } + + inline bool finished() const + { + return (token_list_.end() == token_itr_); + } + + inline void insert_front(token_t::token_type tk_type) + { + if ( + !token_list_.empty() && + (token_list_.end() != token_itr_) + ) + { + token_t t = *token_itr_; + + t.type = tk_type; + token_itr_ = token_list_.insert(token_itr_,t); + } + } + + inline std::string substr(const std::size_t& begin, const std::size_t& end) const + { + const details::char_cptr begin_itr = ((base_itr_ + begin) < s_end_) ? (base_itr_ + begin) : s_end_; + const details::char_cptr end_itr = ((base_itr_ + end ) < s_end_) ? (base_itr_ + end ) : s_end_; + + return std::string(begin_itr,end_itr); + } + + inline std::string remaining() const + { + if (finished()) + return ""; + else if (token_list_.begin() != token_itr_) + return std::string(base_itr_ + (token_itr_ - 1)->position, s_end_); + else + return std::string(base_itr_ + token_itr_->position, s_end_); + } + + private: + + inline bool is_end(details::char_cptr itr) const + { + return (s_end_ == itr); + } + + #ifndef exprtk_disable_comments + inline bool is_comment_start(details::char_cptr itr) const + { + const char_t c0 = *(itr + 0); + const char_t c1 = *(itr + 1); + + if ('#' == c0) + return true; + else if (!is_end(itr + 1)) + { + if (('/' == c0) && ('/' == c1)) return true; + if (('/' == c0) && ('*' == c1)) return true; + } + return false; + } + #else + inline bool is_comment_start(details::char_cptr) const + { + return false; + } + #endif + + inline void skip_whitespace() + { + while (!is_end(s_itr_) && details::is_whitespace(*s_itr_)) + { + ++s_itr_; + } + } + + inline void skip_comments() + { + #ifndef exprtk_disable_comments + // The following comment styles are supported: + // 1. // .... \n + // 2. # .... \n + // 3. /* .... */ + struct test + { + static inline bool comment_start(const char_t c0, const char_t c1, int& mode, int& incr) + { + mode = 0; + if ('#' == c0) { mode = 1; incr = 1; } + else if ('/' == c0) + { + if ('/' == c1) { mode = 1; incr = 2; } + else if ('*' == c1) { mode = 2; incr = 2; } + } + return (0 != mode); + } + + static inline bool comment_end(const char_t c0, const char_t c1, int& mode) + { + if ( + ((1 == mode) && ('\n' == c0)) || + ((2 == mode) && ( '*' == c0) && ('/' == c1)) + ) + { + mode = 0; + return true; + } + else + return false; + } + }; + + int mode = 0; + int increment = 0; + + if (is_end(s_itr_)) + return; + else if (!test::comment_start(*s_itr_, *(s_itr_ + 1), mode, increment)) + return; + + details::char_cptr cmt_start = s_itr_; + + s_itr_ += increment; + + while (!is_end(s_itr_)) + { + if ((1 == mode) && test::comment_end(*s_itr_, 0, mode)) + { + ++s_itr_; + return; + } + + if ((2 == mode)) + { + if (!is_end((s_itr_ + 1)) && test::comment_end(*s_itr_, *(s_itr_ + 1), mode)) + { + s_itr_ += 2; + return; + } + } + + ++s_itr_; + } + + if (2 == mode) + { + token_t t; + t.set_error(token::e_error, cmt_start, cmt_start + mode, base_itr_); + token_list_.push_back(t); + } + #endif + } + + inline bool next_is_digit(const details::char_cptr itr) const + { + return ((itr + 1) != s_end_) && + details::is_digit(*(itr + 1)); + } + + inline void scan_token() + { + const char_t c = *s_itr_; + + if (details::is_whitespace(c)) + { + skip_whitespace(); + return; + } + else if (is_comment_start(s_itr_)) + { + skip_comments(); + return; + } + else if (details::is_operator_char(c)) + { + scan_operator(); + return; + } + else if (details::is_letter(c)) + { + scan_symbol(); + return; + } + else if (('.' == c) && !next_is_digit(s_itr_)) + { + scan_operator(); + return; + } + else if (details::is_digit(c) || ('.' == c)) + { + scan_number(); + return; + } + else if ('$' == c) + { + scan_special_function(); + return; + } + #ifndef exprtk_disable_string_capabilities + else if ('\'' == c) + { + scan_string(); + return; + } + #endif + else if ('~' == c) + { + token_t t; + t.set_symbol(s_itr_, s_itr_ + 1, base_itr_); + token_list_.push_back(t); + ++s_itr_; + return; + } + else + { + token_t t; + t.set_error(token::e_error, s_itr_, s_itr_ + 2, base_itr_); + token_list_.push_back(t); + ++s_itr_; + } + } + + inline void scan_operator() + { + token_t t; + + const char_t c0 = s_itr_[0]; + + if (!is_end(s_itr_ + 1)) + { + const char_t c1 = s_itr_[1]; + + if (!is_end(s_itr_ + 2)) + { + const char_t c2 = s_itr_[2]; + + if ((c0 == '<') && (c1 == '=') && (c2 == '>')) + { + t.set_operator(token_t::e_swap, s_itr_, s_itr_ + 3, base_itr_); + token_list_.push_back(t); + s_itr_ += 3; + return; + } + } + + token_t::token_type ttype = token_t::e_none; + + if ((c0 == '<') && (c1 == '=')) ttype = token_t::e_lte; + else if ((c0 == '>') && (c1 == '=')) ttype = token_t::e_gte; + else if ((c0 == '<') && (c1 == '>')) ttype = token_t::e_ne; + else if ((c0 == '!') && (c1 == '=')) ttype = token_t::e_ne; + else if ((c0 == '=') && (c1 == '=')) ttype = token_t::e_eq; + else if ((c0 == ':') && (c1 == '=')) ttype = token_t::e_assign; + else if ((c0 == '<') && (c1 == '<')) ttype = token_t::e_shl; + else if ((c0 == '>') && (c1 == '>')) ttype = token_t::e_shr; + else if ((c0 == '+') && (c1 == '=')) ttype = token_t::e_addass; + else if ((c0 == '-') && (c1 == '=')) ttype = token_t::e_subass; + else if ((c0 == '*') && (c1 == '=')) ttype = token_t::e_mulass; + else if ((c0 == '/') && (c1 == '=')) ttype = token_t::e_divass; + else if ((c0 == '%') && (c1 == '=')) ttype = token_t::e_modass; + + if (token_t::e_none != ttype) + { + t.set_operator(ttype, s_itr_, s_itr_ + 2, base_itr_); + token_list_.push_back(t); + s_itr_ += 2; + return; + } + } + + if ('<' == c0) + t.set_operator(token_t::e_lt , s_itr_, s_itr_ + 1, base_itr_); + else if ('>' == c0) + t.set_operator(token_t::e_gt , s_itr_, s_itr_ + 1, base_itr_); + else if (';' == c0) + t.set_operator(token_t::e_eof, s_itr_, s_itr_ + 1, base_itr_); + else if ('&' == c0) + t.set_symbol(s_itr_, s_itr_ + 1, base_itr_); + else if ('|' == c0) + t.set_symbol(s_itr_, s_itr_ + 1, base_itr_); + else + t.set_operator(token_t::token_type(c0), s_itr_, s_itr_ + 1, base_itr_); + + token_list_.push_back(t); + ++s_itr_; + } + + inline void scan_symbol() + { + details::char_cptr initial_itr = s_itr_; + + while (!is_end(s_itr_)) + { + if (!details::is_letter_or_digit(*s_itr_) && ('_' != (*s_itr_))) + { + if ('.' != (*s_itr_)) + break; + /* + Permit symbols that contain a 'dot' + Allowed : abc.xyz, a123.xyz, abc.123, abc_.xyz a123_.xyz abc._123 + Disallowed: .abc, abc., abc., abc. + */ + if ( + (s_itr_ != initial_itr) && + !is_end(s_itr_ + 1) && + !details::is_letter_or_digit(*(s_itr_ + 1)) && + ('_' != (*(s_itr_ + 1))) + ) + break; + } + + ++s_itr_; + } + + token_t t; + t.set_symbol(initial_itr, s_itr_, base_itr_); + token_list_.push_back(t); + } + + inline void scan_number() + { + /* + Attempt to match a valid numeric value in one of the following formats: + (01) 123456 + (02) 123456. + (03) 123.456 + (04) 123.456e3 + (05) 123.456E3 + (06) 123.456e+3 + (07) 123.456E+3 + (08) 123.456e-3 + (09) 123.456E-3 + (00) .1234 + (11) .1234e3 + (12) .1234E+3 + (13) .1234e+3 + (14) .1234E-3 + (15) .1234e-3 + */ + + details::char_cptr initial_itr = s_itr_; + bool dot_found = false; + bool e_found = false; + bool post_e_sign_found = false; + bool post_e_digit_found = false; + token_t t; + + while (!is_end(s_itr_)) + { + if ('.' == (*s_itr_)) + { + if (dot_found) + { + t.set_error(token::e_err_number, initial_itr, s_itr_, base_itr_); + token_list_.push_back(t); + + return; + } + + dot_found = true; + ++s_itr_; + + continue; + } + else if ('e' == std::tolower(*s_itr_)) + { + const char_t& c = *(s_itr_ + 1); + + if (is_end(s_itr_ + 1)) + { + t.set_error(token::e_err_number, initial_itr, s_itr_, base_itr_); + token_list_.push_back(t); + + return; + } + else if ( + ('+' != c) && + ('-' != c) && + !details::is_digit(c) + ) + { + t.set_error(token::e_err_number, initial_itr, s_itr_, base_itr_); + token_list_.push_back(t); + + return; + } + + e_found = true; + ++s_itr_; + + continue; + } + else if (e_found && details::is_sign(*s_itr_) && !post_e_digit_found) + { + if (post_e_sign_found) + { + t.set_error(token::e_err_number, initial_itr, s_itr_, base_itr_); + token_list_.push_back(t); + + return; + } + + post_e_sign_found = true; + ++s_itr_; + + continue; + } + else if (e_found && details::is_digit(*s_itr_)) + { + post_e_digit_found = true; + ++s_itr_; + + continue; + } + else if (('.' != (*s_itr_)) && !details::is_digit(*s_itr_)) + break; + else + ++s_itr_; + } + + t.set_numeric(initial_itr, s_itr_, base_itr_); + token_list_.push_back(t); + + return; + } + + inline void scan_special_function() + { + details::char_cptr initial_itr = s_itr_; + token_t t; + + // $fdd(x,x,x) = at least 11 chars + if (std::distance(s_itr_,s_end_) < 11) + { + t.set_error( + token::e_err_sfunc, + initial_itr, std::min(initial_itr + 11, s_end_), + base_itr_); + token_list_.push_back(t); + + return; + } + + if ( + !(('$' == *s_itr_) && + (details::imatch ('f',*(s_itr_ + 1))) && + (details::is_digit(*(s_itr_ + 2))) && + (details::is_digit(*(s_itr_ + 3)))) + ) + { + t.set_error( + token::e_err_sfunc, + initial_itr, std::min(initial_itr + 4, s_end_), + base_itr_); + token_list_.push_back(t); + + return; + } + + s_itr_ += 4; // $fdd = 4chars + + t.set_symbol(initial_itr, s_itr_, base_itr_); + token_list_.push_back(t); + + return; + } + + #ifndef exprtk_disable_string_capabilities + inline void scan_string() + { + details::char_cptr initial_itr = s_itr_ + 1; + token_t t; + + if (std::distance(s_itr_,s_end_) < 2) + { + t.set_error(token::e_err_string, s_itr_, s_end_, base_itr_); + token_list_.push_back(t); + + return; + } + + ++s_itr_; + + bool escaped_found = false; + bool escaped = false; + + while (!is_end(s_itr_)) + { + if (!details::is_valid_string_char(*s_itr_)) + { + t.set_error(token::e_err_string, initial_itr, s_itr_, base_itr_); + token_list_.push_back(t); + + return; + } + else if (!escaped && ('\\' == *s_itr_)) + { + escaped_found = true; + escaped = true; + ++s_itr_; + + continue; + } + else if (!escaped) + { + if ('\'' == *s_itr_) + break; + } + else if (escaped) + { + if ( + !is_end(s_itr_) && ('0' == *(s_itr_)) && + ((s_itr_ + 4) <= s_end_) + ) + { + const bool x_separator = ('X' == std::toupper(*(s_itr_ + 1))); + + const bool both_digits = details::is_hex_digit(*(s_itr_ + 2)) && + details::is_hex_digit(*(s_itr_ + 3)) ; + + if (!(x_separator && both_digits)) + { + t.set_error(token::e_err_string, initial_itr, s_itr_, base_itr_); + token_list_.push_back(t); + + return; + } + else + s_itr_ += 3; + } + + escaped = false; + } + + ++s_itr_; + } + + if (is_end(s_itr_)) + { + t.set_error(token::e_err_string, initial_itr, s_itr_, base_itr_); + token_list_.push_back(t); + + return; + } + + if (!escaped_found) + t.set_string(initial_itr, s_itr_, base_itr_); + else + { + std::string parsed_string(initial_itr,s_itr_); + + if (!details::cleanup_escapes(parsed_string)) + { + t.set_error(token::e_err_string, initial_itr, s_itr_, base_itr_); + token_list_.push_back(t); + + return; + } + + t.set_string( + parsed_string, + static_cast(std::distance(base_itr_,initial_itr))); + } + + token_list_.push_back(t); + ++s_itr_; + + return; + } + #endif + + private: + + token_list_t token_list_; + token_list_itr_t token_itr_; + token_list_itr_t store_token_itr_; + token_t eof_token_; + details::char_cptr base_itr_; + details::char_cptr s_itr_; + details::char_cptr s_end_; + + friend class token_scanner; + friend class token_modifier; + friend class token_inserter; + friend class token_joiner; + }; // class generator + + class helper_interface + { + public: + + virtual void init() { } + virtual void reset() { } + virtual bool result() { return true; } + virtual std::size_t process(generator&) { return 0; } + virtual ~helper_interface() { } + }; + + class token_scanner : public helper_interface + { + public: + + virtual ~token_scanner() exprtk_override + {} + + explicit token_scanner(const std::size_t& stride) + : stride_(stride) + { + if (stride > 4) + { + throw std::invalid_argument("token_scanner() - Invalid stride value"); + } + } + + inline std::size_t process(generator& g) exprtk_override + { + if (g.token_list_.size() >= stride_) + { + for (std::size_t i = 0; i < (g.token_list_.size() - stride_ + 1); ++i) + { + token t; + + switch (stride_) + { + case 1 : + { + const token& t0 = g.token_list_[i]; + + if (!operator()(t0)) + { + return 0; + } + } + break; + + case 2 : + { + const token& t0 = g.token_list_[i ]; + const token& t1 = g.token_list_[i + 1]; + + if (!operator()(t0, t1)) + { + return 0; + } + } + break; + + case 3 : + { + const token& t0 = g.token_list_[i ]; + const token& t1 = g.token_list_[i + 1]; + const token& t2 = g.token_list_[i + 2]; + + if (!operator()(t0, t1, t2)) + { + return 0; + } + } + break; + + case 4 : + { + const token& t0 = g.token_list_[i ]; + const token& t1 = g.token_list_[i + 1]; + const token& t2 = g.token_list_[i + 2]; + const token& t3 = g.token_list_[i + 3]; + + if (!operator()(t0, t1, t2, t3)) + { + return 0; + } + } + break; + } + } + } + + return 0; + } + + virtual bool operator() (const token&) + { + return false; + } + + virtual bool operator() (const token&, const token&) + { + return false; + } + + virtual bool operator() (const token&, const token&, const token&) + { + return false; + } + + virtual bool operator() (const token&, const token&, const token&, const token&) + { + return false; + } + + private: + + const std::size_t stride_; + }; // class token_scanner + + class token_modifier : public helper_interface + { + public: + + inline std::size_t process(generator& g) exprtk_override + { + std::size_t changes = 0; + + for (std::size_t i = 0; i < g.token_list_.size(); ++i) + { + if (modify(g.token_list_[i])) changes++; + } + + return changes; + } + + virtual bool modify(token& t) = 0; + }; + + class token_inserter : public helper_interface + { + public: + + explicit token_inserter(const std::size_t& stride) + : stride_(stride) + { + if (stride > 5) + { + throw std::invalid_argument("token_inserter() - Invalid stride value"); + } + } + + inline std::size_t process(generator& g) exprtk_override + { + if (g.token_list_.empty()) + return 0; + else if (g.token_list_.size() < stride_) + return 0; + + std::size_t changes = 0; + + typedef std::pair insert_t; + std::vector insert_list; + insert_list.reserve(10000); + + for (std::size_t i = 0; i < (g.token_list_.size() - stride_ + 1); ++i) + { + int insert_index = -1; + token t; + + switch (stride_) + { + case 1 : insert_index = insert(g.token_list_[i],t); + break; + + case 2 : insert_index = insert(g.token_list_[i], g.token_list_[i + 1], t); + break; + + case 3 : insert_index = insert(g.token_list_[i], g.token_list_[i + 1], g.token_list_[i + 2], t); + break; + + case 4 : insert_index = insert(g.token_list_[i], g.token_list_[i + 1], g.token_list_[i + 2], g.token_list_[i + 3], t); + break; + + case 5 : insert_index = insert(g.token_list_[i], g.token_list_[i + 1], g.token_list_[i + 2], g.token_list_[i + 3], g.token_list_[i + 4], t); + break; + } + + if ((insert_index >= 0) && (insert_index <= (static_cast(stride_) + 1))) + { + insert_list.push_back(insert_t(i, t)); + changes++; + } + } + + if (!insert_list.empty()) + { + generator::token_list_t token_list; + + std::size_t insert_index = 0; + + for (std::size_t i = 0; i < g.token_list_.size(); ++i) + { + token_list.push_back(g.token_list_[i]); + + if ( + (insert_index < insert_list.size()) && + (insert_list[insert_index].first == i) + ) + { + token_list.push_back(insert_list[insert_index].second); + insert_index++; + } + } + + std::swap(g.token_list_,token_list); + } + + return changes; + } + + #define token_inserter_empty_body \ + { \ + return -1; \ + } \ + + inline virtual int insert(const token&, token&) + token_inserter_empty_body + + inline virtual int insert(const token&, const token&, token&) + token_inserter_empty_body + + inline virtual int insert(const token&, const token&, const token&, token&) + token_inserter_empty_body + + inline virtual int insert(const token&, const token&, const token&, const token&, token&) + token_inserter_empty_body + + inline virtual int insert(const token&, const token&, const token&, const token&, const token&, token&) + token_inserter_empty_body + + #undef token_inserter_empty_body + + private: + + const std::size_t stride_; + }; + + class token_joiner : public helper_interface + { + public: + + explicit token_joiner(const std::size_t& stride) + : stride_(stride) + {} + + inline std::size_t process(generator& g) exprtk_override + { + if (g.token_list_.empty()) + return 0; + + switch (stride_) + { + case 2 : return process_stride_2(g); + case 3 : return process_stride_3(g); + default : return 0; + } + } + + virtual bool join(const token&, const token&, token&) { return false; } + virtual bool join(const token&, const token&, const token&, token&) { return false; } + + private: + + inline std::size_t process_stride_2(generator& g) + { + if (g.token_list_.size() < 2) + return 0; + + std::size_t changes = 0; + + generator::token_list_t token_list; + token_list.reserve(10000); + + for (int i = 0; i < static_cast(g.token_list_.size() - 1); ++i) + { + token t; + + for ( ; ; ) + { + if (!join(g[i], g[i + 1], t)) + { + token_list.push_back(g[i]); + break; + } + + token_list.push_back(t); + + ++changes; + + i += 2; + + if (static_cast(i) >= (g.token_list_.size() - 1)) + break; + } + } + + token_list.push_back(g.token_list_.back()); + + assert(token_list.size() <= g.token_list_.size()); + + std::swap(token_list, g.token_list_); + + return changes; + } + + inline std::size_t process_stride_3(generator& g) + { + if (g.token_list_.size() < 3) + return 0; + + std::size_t changes = 0; + + generator::token_list_t token_list; + token_list.reserve(10000); + + for (int i = 0; i < static_cast(g.token_list_.size() - 2); ++i) + { + token t; + + for ( ; ; ) + { + if (!join(g[i], g[i + 1], g[i + 2], t)) + { + token_list.push_back(g[i]); + break; + } + + token_list.push_back(t); + + ++changes; + + i += 3; + + if (static_cast(i) >= (g.token_list_.size() - 2)) + break; + } + } + + token_list.push_back(*(g.token_list_.begin() + g.token_list_.size() - 2)); + token_list.push_back(*(g.token_list_.begin() + g.token_list_.size() - 1)); + + assert(token_list.size() <= g.token_list_.size()); + + std::swap(token_list, g.token_list_); + + return changes; + } + + const std::size_t stride_; + }; + + namespace helper + { + + inline void dump(const lexer::generator& generator) + { + for (std::size_t i = 0; i < generator.size(); ++i) + { + const lexer::token& t = generator[i]; + printf("Token[%02d] @ %03d %6s --> '%s'\n", + static_cast(i), + static_cast(t.position), + t.to_str(t.type).c_str(), + t.value.c_str()); + } + } + + class commutative_inserter : public lexer::token_inserter + { + public: + + using lexer::token_inserter::insert; + + commutative_inserter() + : lexer::token_inserter(2) + {} + + inline void ignore_symbol(const std::string& symbol) + { + ignore_set_.insert(symbol); + } + + inline int insert(const lexer::token& t0, const lexer::token& t1, lexer::token& new_token) exprtk_override + { + bool match = false; + new_token.type = lexer::token::e_mul; + new_token.value = "*"; + new_token.position = t1.position; + + if (t0.type == lexer::token::e_symbol) + { + if (ignore_set_.end() != ignore_set_.find(t0.value)) + { + return -1; + } + else if (!t0.value.empty() && ('$' == t0.value[0])) + { + return -1; + } + } + + if (t1.type == lexer::token::e_symbol) + { + if (ignore_set_.end() != ignore_set_.find(t1.value)) + { + return -1; + } + } + if ((t0.type == lexer::token::e_number ) && (t1.type == lexer::token::e_symbol )) match = true; + else if ((t0.type == lexer::token::e_number ) && (t1.type == lexer::token::e_lbracket )) match = true; + else if ((t0.type == lexer::token::e_number ) && (t1.type == lexer::token::e_lcrlbracket)) match = true; + else if ((t0.type == lexer::token::e_number ) && (t1.type == lexer::token::e_lsqrbracket)) match = true; + else if ((t0.type == lexer::token::e_symbol ) && (t1.type == lexer::token::e_number )) match = true; + else if ((t0.type == lexer::token::e_rbracket ) && (t1.type == lexer::token::e_number )) match = true; + else if ((t0.type == lexer::token::e_rcrlbracket) && (t1.type == lexer::token::e_number )) match = true; + else if ((t0.type == lexer::token::e_rsqrbracket) && (t1.type == lexer::token::e_number )) match = true; + else if ((t0.type == lexer::token::e_rbracket ) && (t1.type == lexer::token::e_symbol )) match = true; + else if ((t0.type == lexer::token::e_rcrlbracket) && (t1.type == lexer::token::e_symbol )) match = true; + else if ((t0.type == lexer::token::e_rsqrbracket) && (t1.type == lexer::token::e_symbol )) match = true; + else if ((t0.type == lexer::token::e_symbol ) && (t1.type == lexer::token::e_symbol )) match = true; + + return (match) ? 1 : -1; + } + + private: + + std::set ignore_set_; + }; + + class operator_joiner exprtk_final : public token_joiner + { + public: + + explicit operator_joiner(const std::size_t& stride) + : token_joiner(stride) + {} + + inline bool join(const lexer::token& t0, const lexer::token& t1, lexer::token& t) exprtk_override + { + // ': =' --> ':=' + if ((t0.type == lexer::token::e_colon) && (t1.type == lexer::token::e_eq)) + { + t.type = lexer::token::e_assign; + t.value = ":="; + t.position = t0.position; + + return true; + } + // '+ =' --> '+=' + else if ((t0.type == lexer::token::e_add) && (t1.type == lexer::token::e_eq)) + { + t.type = lexer::token::e_addass; + t.value = "+="; + t.position = t0.position; + + return true; + } + // '- =' --> '-=' + else if ((t0.type == lexer::token::e_sub) && (t1.type == lexer::token::e_eq)) + { + t.type = lexer::token::e_subass; + t.value = "-="; + t.position = t0.position; + + return true; + } + // '* =' --> '*=' + else if ((t0.type == lexer::token::e_mul) && (t1.type == lexer::token::e_eq)) + { + t.type = lexer::token::e_mulass; + t.value = "*="; + t.position = t0.position; + + return true; + } + // '/ =' --> '/=' + else if ((t0.type == lexer::token::e_div) && (t1.type == lexer::token::e_eq)) + { + t.type = lexer::token::e_divass; + t.value = "/="; + t.position = t0.position; + + return true; + } + // '% =' --> '%=' + else if ((t0.type == lexer::token::e_mod) && (t1.type == lexer::token::e_eq)) + { + t.type = lexer::token::e_modass; + t.value = "%="; + t.position = t0.position; + + return true; + } + // '> =' --> '>=' + else if ((t0.type == lexer::token::e_gt) && (t1.type == lexer::token::e_eq)) + { + t.type = lexer::token::e_gte; + t.value = ">="; + t.position = t0.position; + + return true; + } + // '< =' --> '<=' + else if ((t0.type == lexer::token::e_lt) && (t1.type == lexer::token::e_eq)) + { + t.type = lexer::token::e_lte; + t.value = "<="; + t.position = t0.position; + + return true; + } + // '= =' --> '==' + else if ((t0.type == lexer::token::e_eq) && (t1.type == lexer::token::e_eq)) + { + t.type = lexer::token::e_eq; + t.value = "=="; + t.position = t0.position; + + return true; + } + // '! =' --> '!=' + else if ((static_cast(t0.type) == '!') && (t1.type == lexer::token::e_eq)) + { + t.type = lexer::token::e_ne; + t.value = "!="; + t.position = t0.position; + + return true; + } + // '< >' --> '<>' + else if ((t0.type == lexer::token::e_lt) && (t1.type == lexer::token::e_gt)) + { + t.type = lexer::token::e_ne; + t.value = "<>"; + t.position = t0.position; + + return true; + } + // '<= >' --> '<=>' + else if ((t0.type == lexer::token::e_lte) && (t1.type == lexer::token::e_gt)) + { + t.type = lexer::token::e_swap; + t.value = "<=>"; + t.position = t0.position; + + return true; + } + // '+ -' --> '-' + else if ((t0.type == lexer::token::e_add) && (t1.type == lexer::token::e_sub)) + { + t.type = lexer::token::e_sub; + t.value = "-"; + t.position = t0.position; + + return true; + } + // '- +' --> '-' + else if ((t0.type == lexer::token::e_sub) && (t1.type == lexer::token::e_add)) + { + t.type = lexer::token::e_sub; + t.value = "-"; + t.position = t0.position; + + return true; + } + // '- -' --> '+' + else if ((t0.type == lexer::token::e_sub) && (t1.type == lexer::token::e_sub)) + { + /* + Note: May need to reconsider this when wanting to implement + pre/postfix decrement operator + */ + t.type = lexer::token::e_add; + t.value = "+"; + t.position = t0.position; + + return true; + } + else + return false; + } + + inline bool join(const lexer::token& t0, + const lexer::token& t1, + const lexer::token& t2, + lexer::token& t) exprtk_override + { + // '[ * ]' --> '[*]' + if ( + (t0.type == lexer::token::e_lsqrbracket) && + (t1.type == lexer::token::e_mul ) && + (t2.type == lexer::token::e_rsqrbracket) + ) + { + t.type = lexer::token::e_symbol; + t.value = "[*]"; + t.position = t0.position; + + return true; + } + else + return false; + } + }; + + class bracket_checker exprtk_final : public lexer::token_scanner + { + public: + + using lexer::token_scanner::operator(); + + bracket_checker() + : token_scanner(1) + , state_(true) + {} + + bool result() exprtk_override + { + if (!stack_.empty()) + { + lexer::token t; + t.value = stack_.top().first; + t.position = stack_.top().second; + error_token_ = t; + state_ = false; + + return false; + } + else + return state_; + } + + lexer::token error_token() const + { + return error_token_; + } + + void reset() exprtk_override + { + // Why? because msvc doesn't support swap properly. + stack_ = std::stack >(); + state_ = true; + error_token_.clear(); + } + + bool operator() (const lexer::token& t) exprtk_override + { + if ( + !t.value.empty() && + (lexer::token::e_string != t.type) && + (lexer::token::e_symbol != t.type) && + exprtk::details::is_bracket(t.value[0]) + ) + { + details::char_t c = t.value[0]; + + if (t.type == lexer::token::e_lbracket ) stack_.push(std::make_pair(')',t.position)); + else if (t.type == lexer::token::e_lcrlbracket) stack_.push(std::make_pair('}',t.position)); + else if (t.type == lexer::token::e_lsqrbracket) stack_.push(std::make_pair(']',t.position)); + else if (exprtk::details::is_right_bracket(c)) + { + if (stack_.empty()) + { + state_ = false; + error_token_ = t; + + return false; + } + else if (c != stack_.top().first) + { + state_ = false; + error_token_ = t; + + return false; + } + else + stack_.pop(); + } + } + + return true; + } + + private: + + bool state_; + std::stack > stack_; + lexer::token error_token_; + }; + + template + class numeric_checker exprtk_final : public lexer::token_scanner + { + public: + + using lexer::token_scanner::operator(); + + numeric_checker() + : token_scanner (1) + , current_index_(0) + {} + + bool result() exprtk_override + { + return error_list_.empty(); + } + + void reset() exprtk_override + { + error_list_.clear(); + current_index_ = 0; + } + + bool operator() (const lexer::token& t) exprtk_override + { + if (token::e_number == t.type) + { + T v; + + if (!exprtk::details::string_to_real(t.value,v)) + { + error_list_.push_back(current_index_); + } + } + + ++current_index_; + + return true; + } + + std::size_t error_count() const + { + return error_list_.size(); + } + + std::size_t error_index(const std::size_t& i) const + { + if (i < error_list_.size()) + return error_list_[i]; + else + return std::numeric_limits::max(); + } + + void clear_errors() + { + error_list_.clear(); + } + + private: + + std::size_t current_index_; + std::vector error_list_; + }; + + class symbol_replacer exprtk_final : public lexer::token_modifier + { + private: + + typedef std::map,details::ilesscompare> replace_map_t; + + public: + + bool remove(const std::string& target_symbol) + { + const replace_map_t::iterator itr = replace_map_.find(target_symbol); + + if (replace_map_.end() == itr) + return false; + + replace_map_.erase(itr); + + return true; + } + + bool add_replace(const std::string& target_symbol, + const std::string& replace_symbol, + const lexer::token::token_type token_type = lexer::token::e_symbol) + { + const replace_map_t::iterator itr = replace_map_.find(target_symbol); + + if (replace_map_.end() != itr) + { + return false; + } + + replace_map_[target_symbol] = std::make_pair(replace_symbol,token_type); + + return true; + } + + void clear() + { + replace_map_.clear(); + } + + private: + + bool modify(lexer::token& t) exprtk_override + { + if (lexer::token::e_symbol == t.type) + { + if (replace_map_.empty()) + return false; + + const replace_map_t::iterator itr = replace_map_.find(t.value); + + if (replace_map_.end() != itr) + { + t.value = itr->second.first; + t.type = itr->second.second; + + return true; + } + } + + return false; + } + + replace_map_t replace_map_; + }; + + class sequence_validator exprtk_final : public lexer::token_scanner + { + private: + + typedef std::pair token_pair_t; + typedef std::set set_t; + + public: + + using lexer::token_scanner::operator(); + + sequence_validator() + : lexer::token_scanner(2) + { + add_invalid(lexer::token::e_number, lexer::token::e_number); + add_invalid(lexer::token::e_string, lexer::token::e_string); + add_invalid(lexer::token::e_number, lexer::token::e_string); + add_invalid(lexer::token::e_string, lexer::token::e_number); + + add_invalid_set1(lexer::token::e_assign ); + add_invalid_set1(lexer::token::e_shr ); + add_invalid_set1(lexer::token::e_shl ); + add_invalid_set1(lexer::token::e_lte ); + add_invalid_set1(lexer::token::e_ne ); + add_invalid_set1(lexer::token::e_gte ); + add_invalid_set1(lexer::token::e_lt ); + add_invalid_set1(lexer::token::e_gt ); + add_invalid_set1(lexer::token::e_eq ); + add_invalid_set1(lexer::token::e_comma ); + add_invalid_set1(lexer::token::e_add ); + add_invalid_set1(lexer::token::e_sub ); + add_invalid_set1(lexer::token::e_div ); + add_invalid_set1(lexer::token::e_mul ); + add_invalid_set1(lexer::token::e_mod ); + add_invalid_set1(lexer::token::e_pow ); + add_invalid_set1(lexer::token::e_colon ); + add_invalid_set1(lexer::token::e_ternary); + } + + bool result() exprtk_override + { + return error_list_.empty(); + } + + bool operator() (const lexer::token& t0, const lexer::token& t1) exprtk_override + { + const set_t::value_type p = std::make_pair(t0.type,t1.type); + + if (invalid_bracket_check(t0.type,t1.type)) + { + error_list_.push_back(std::make_pair(t0,t1)); + } + else if (invalid_comb_.find(p) != invalid_comb_.end()) + { + error_list_.push_back(std::make_pair(t0,t1)); + } + + return true; + } + + std::size_t error_count() const + { + return error_list_.size(); + } + + std::pair error(const std::size_t index) + { + if (index < error_list_.size()) + { + return error_list_[index]; + } + else + { + static const lexer::token error_token; + return std::make_pair(error_token,error_token); + } + } + + void clear_errors() + { + error_list_.clear(); + } + + private: + + void add_invalid(const lexer::token::token_type base, const lexer::token::token_type t) + { + invalid_comb_.insert(std::make_pair(base,t)); + } + + void add_invalid_set1(const lexer::token::token_type t) + { + add_invalid(t, lexer::token::e_assign); + add_invalid(t, lexer::token::e_shr ); + add_invalid(t, lexer::token::e_shl ); + add_invalid(t, lexer::token::e_lte ); + add_invalid(t, lexer::token::e_ne ); + add_invalid(t, lexer::token::e_gte ); + add_invalid(t, lexer::token::e_lt ); + add_invalid(t, lexer::token::e_gt ); + add_invalid(t, lexer::token::e_eq ); + add_invalid(t, lexer::token::e_comma ); + add_invalid(t, lexer::token::e_div ); + add_invalid(t, lexer::token::e_mul ); + add_invalid(t, lexer::token::e_mod ); + add_invalid(t, lexer::token::e_pow ); + add_invalid(t, lexer::token::e_colon ); + } + + bool invalid_bracket_check(const lexer::token::token_type base, const lexer::token::token_type t) + { + if (details::is_right_bracket(static_cast(base))) + { + switch (t) + { + case lexer::token::e_assign : return (']' != base); + case lexer::token::e_string : return (')' != base); + default : return false; + } + } + else if (details::is_left_bracket(static_cast(base))) + { + if (details::is_right_bracket(static_cast(t))) + return false; + else if (details::is_left_bracket(static_cast(t))) + return false; + else + { + switch (t) + { + case lexer::token::e_number : return false; + case lexer::token::e_symbol : return false; + case lexer::token::e_string : return false; + case lexer::token::e_add : return false; + case lexer::token::e_sub : return false; + case lexer::token::e_colon : return false; + case lexer::token::e_ternary : return false; + default : return true ; + } + } + } + else if (details::is_right_bracket(static_cast(t))) + { + switch (base) + { + case lexer::token::e_number : return false; + case lexer::token::e_symbol : return false; + case lexer::token::e_string : return false; + case lexer::token::e_eof : return false; + case lexer::token::e_colon : return false; + case lexer::token::e_ternary : return false; + default : return true ; + } + } + else if (details::is_left_bracket(static_cast(t))) + { + switch (base) + { + case lexer::token::e_rbracket : return true; + case lexer::token::e_rsqrbracket : return true; + case lexer::token::e_rcrlbracket : return true; + default : return false; + } + } + + return false; + } + + set_t invalid_comb_; + std::vector > error_list_; + }; + + class sequence_validator_3tokens exprtk_final : public lexer::token_scanner + { + private: + + typedef lexer::token::token_type token_t; + typedef std::pair > token_triplet_t; + typedef std::set set_t; + + public: + + using lexer::token_scanner::operator(); + + sequence_validator_3tokens() + : lexer::token_scanner(3) + { + add_invalid(lexer::token::e_number , lexer::token::e_number , lexer::token::e_number); + add_invalid(lexer::token::e_string , lexer::token::e_string , lexer::token::e_string); + add_invalid(lexer::token::e_comma , lexer::token::e_comma , lexer::token::e_comma ); + + add_invalid(lexer::token::e_add , lexer::token::e_add , lexer::token::e_add ); + add_invalid(lexer::token::e_sub , lexer::token::e_sub , lexer::token::e_sub ); + add_invalid(lexer::token::e_div , lexer::token::e_div , lexer::token::e_div ); + add_invalid(lexer::token::e_mul , lexer::token::e_mul , lexer::token::e_mul ); + add_invalid(lexer::token::e_mod , lexer::token::e_mod , lexer::token::e_mod ); + add_invalid(lexer::token::e_pow , lexer::token::e_pow , lexer::token::e_pow ); + + add_invalid(lexer::token::e_add , lexer::token::e_sub , lexer::token::e_add ); + add_invalid(lexer::token::e_sub , lexer::token::e_add , lexer::token::e_sub ); + add_invalid(lexer::token::e_div , lexer::token::e_mul , lexer::token::e_div ); + add_invalid(lexer::token::e_mul , lexer::token::e_div , lexer::token::e_mul ); + add_invalid(lexer::token::e_mod , lexer::token::e_pow , lexer::token::e_mod ); + add_invalid(lexer::token::e_pow , lexer::token::e_mod , lexer::token::e_pow ); + } + + bool result() exprtk_override + { + return error_list_.empty(); + } + + bool operator() (const lexer::token& t0, const lexer::token& t1, const lexer::token& t2) exprtk_override + { + const set_t::value_type p = std::make_pair(t0.type,std::make_pair(t1.type,t2.type)); + + if (invalid_comb_.find(p) != invalid_comb_.end()) + { + error_list_.push_back(std::make_pair(t0,t1)); + } + + return true; + } + + std::size_t error_count() const + { + return error_list_.size(); + } + + std::pair error(const std::size_t index) + { + if (index < error_list_.size()) + { + return error_list_[index]; + } + else + { + static const lexer::token error_token; + return std::make_pair(error_token,error_token); + } + } + + void clear_errors() + { + error_list_.clear(); + } + + private: + + void add_invalid(const token_t t0, const token_t t1, const token_t t2) + { + invalid_comb_.insert(std::make_pair(t0,std::make_pair(t1,t2))); + } + + set_t invalid_comb_; + std::vector > error_list_; + }; + + struct helper_assembly + { + inline bool register_scanner(lexer::token_scanner* scanner) + { + if (token_scanner_list.end() != std::find(token_scanner_list.begin(), + token_scanner_list.end (), + scanner)) + { + return false; + } + + token_scanner_list.push_back(scanner); + + return true; + } + + inline bool register_modifier(lexer::token_modifier* modifier) + { + if (token_modifier_list.end() != std::find(token_modifier_list.begin(), + token_modifier_list.end (), + modifier)) + { + return false; + } + + token_modifier_list.push_back(modifier); + + return true; + } + + inline bool register_joiner(lexer::token_joiner* joiner) + { + if (token_joiner_list.end() != std::find(token_joiner_list.begin(), + token_joiner_list.end (), + joiner)) + { + return false; + } + + token_joiner_list.push_back(joiner); + + return true; + } + + inline bool register_inserter(lexer::token_inserter* inserter) + { + if (token_inserter_list.end() != std::find(token_inserter_list.begin(), + token_inserter_list.end (), + inserter)) + { + return false; + } + + token_inserter_list.push_back(inserter); + + return true; + } + + inline bool run_modifiers(lexer::generator& g) + { + error_token_modifier = reinterpret_cast(0); + + for (std::size_t i = 0; i < token_modifier_list.size(); ++i) + { + lexer::token_modifier& modifier = (*token_modifier_list[i]); + + modifier.reset(); + modifier.process(g); + + if (!modifier.result()) + { + error_token_modifier = token_modifier_list[i]; + + return false; + } + } + + return true; + } + + inline bool run_joiners(lexer::generator& g) + { + error_token_joiner = reinterpret_cast(0); + + for (std::size_t i = 0; i < token_joiner_list.size(); ++i) + { + lexer::token_joiner& joiner = (*token_joiner_list[i]); + + joiner.reset(); + joiner.process(g); + + if (!joiner.result()) + { + error_token_joiner = token_joiner_list[i]; + + return false; + } + } + + return true; + } + + inline bool run_inserters(lexer::generator& g) + { + error_token_inserter = reinterpret_cast(0); + + for (std::size_t i = 0; i < token_inserter_list.size(); ++i) + { + lexer::token_inserter& inserter = (*token_inserter_list[i]); + + inserter.reset(); + inserter.process(g); + + if (!inserter.result()) + { + error_token_inserter = token_inserter_list[i]; + + return false; + } + } + + return true; + } + + inline bool run_scanners(lexer::generator& g) + { + error_token_scanner = reinterpret_cast(0); + + for (std::size_t i = 0; i < token_scanner_list.size(); ++i) + { + lexer::token_scanner& scanner = (*token_scanner_list[i]); + + scanner.reset(); + scanner.process(g); + + if (!scanner.result()) + { + error_token_scanner = token_scanner_list[i]; + + return false; + } + } + + return true; + } + + std::vector token_scanner_list; + std::vector token_modifier_list; + std::vector token_joiner_list; + std::vector token_inserter_list; + + lexer::token_scanner* error_token_scanner; + lexer::token_modifier* error_token_modifier; + lexer::token_joiner* error_token_joiner; + lexer::token_inserter* error_token_inserter; + }; + } + + class parser_helper + { + public: + + typedef token token_t; + typedef generator generator_t; + + inline bool init(const std::string& str) + { + if (!lexer_.process(str)) + { + return false; + } + + lexer_.begin(); + + next_token(); + + return true; + } + + inline generator_t& lexer() + { + return lexer_; + } + + inline const generator_t& lexer() const + { + return lexer_; + } + + inline void store_token() + { + lexer_.store(); + store_current_token_ = current_token_; + } + + inline void restore_token() + { + lexer_.restore(); + current_token_ = store_current_token_; + } + + inline void next_token() + { + current_token_ = lexer_.next_token(); + } + + inline const token_t& current_token() const + { + return current_token_; + } + + inline const token_t& peek_next_token() + { + return lexer_.peek_next_token(); + } + + enum token_advance_mode + { + e_hold = 0, + e_advance = 1 + }; + + inline void advance_token(const token_advance_mode mode) + { + if (e_advance == mode) + { + next_token(); + } + } + + inline bool token_is(const token_t::token_type& ttype, const token_advance_mode mode = e_advance) + { + if (current_token().type != ttype) + { + return false; + } + + advance_token(mode); + + return true; + } + + inline bool token_is(const token_t::token_type& ttype, + const std::string& value, + const token_advance_mode mode = e_advance) + { + if ( + (current_token().type != ttype) || + !exprtk::details::imatch(value,current_token().value) + ) + { + return false; + } + + advance_token(mode); + + return true; + } + + inline bool token_is(const std::string& value, + const token_advance_mode mode = e_advance) + { + if (!exprtk::details::imatch(value,current_token().value)) + { + return false; + } + + advance_token(mode); + + return true; + } + + inline bool token_is_arithmetic_opr(const token_advance_mode mode = e_advance) + { + switch (current_token().type) + { + case token_t::e_add : + case token_t::e_sub : + case token_t::e_div : + case token_t::e_mul : + case token_t::e_mod : + case token_t::e_pow : break; + default : return false; + } + + advance_token(mode); + + return true; + } + + inline bool token_is_ineq_opr(const token_advance_mode mode = e_advance) + { + switch (current_token().type) + { + case token_t::e_eq : + case token_t::e_lte : + case token_t::e_ne : + case token_t::e_gte : + case token_t::e_lt : + case token_t::e_gt : break; + default : return false; + } + + advance_token(mode); + + return true; + } + + inline bool token_is_left_bracket(const token_advance_mode mode = e_advance) + { + switch (current_token().type) + { + case token_t::e_lbracket : + case token_t::e_lcrlbracket : + case token_t::e_lsqrbracket : break; + default : return false; + } + + advance_token(mode); + + return true; + } + + inline bool token_is_right_bracket(const token_advance_mode mode = e_advance) + { + switch (current_token().type) + { + case token_t::e_rbracket : + case token_t::e_rcrlbracket : + case token_t::e_rsqrbracket : break; + default : return false; + } + + advance_token(mode); + + return true; + } + + inline bool token_is_bracket(const token_advance_mode mode = e_advance) + { + switch (current_token().type) + { + case token_t::e_rbracket : + case token_t::e_rcrlbracket : + case token_t::e_rsqrbracket : + case token_t::e_lbracket : + case token_t::e_lcrlbracket : + case token_t::e_lsqrbracket : break; + default : return false; + } + + advance_token(mode); + + return true; + } + + inline bool token_is_loop(const token_advance_mode mode = e_advance) + { + return token_is("for" , mode) || + token_is("while" , mode) || + token_is("repeat", mode) ; + } + + inline bool peek_token_is(const token_t::token_type& ttype) + { + return (lexer_.peek_next_token().type == ttype); + } + + inline bool peek_token_is(const std::string& s) + { + return (exprtk::details::imatch(lexer_.peek_next_token().value,s)); + } + + private: + + generator_t lexer_; + token_t current_token_; + token_t store_current_token_; + }; + } + + template + class vector_view + { + public: + + typedef T* data_ptr_t; + + vector_view(data_ptr_t data, const std::size_t& size) + : base_size_(size) + , size_(size) + , data_(data) + , data_ref_(0) + { + assert(size_ > 0); + } + + vector_view(const vector_view& vv) + : base_size_(vv.base_size_) + , size_(vv.size_) + , data_(vv.data_) + , data_ref_(0) + { + assert(size_ > 0); + } + + inline void rebase(data_ptr_t data) + { + data_ = data; + + if (!data_ref_.empty()) + { + for (std::size_t i = 0; i < data_ref_.size(); ++i) + { + (*data_ref_[i]) = data; + } + } + } + + inline data_ptr_t data() const + { + return data_; + } + + inline std::size_t base_size() const + { + return base_size_; + } + + inline std::size_t size() const + { + return size_; + } + + inline const T& operator[](const std::size_t index) const + { + assert(index < size_); + return data_[index]; + } + + inline T& operator[](const std::size_t index) + { + assert(index < size_); + return data_[index]; + } + + void set_ref(data_ptr_t* data_ref) + { + data_ref_.push_back(data_ref); + exprtk_debug(("vector_view::set_ref() - data_ref: %p data_ref_.size(): %d\n", + reinterpret_cast(data_ref), + static_cast(data_ref_.size()))); + } + + void remove_ref(data_ptr_t* data_ref) + { + data_ref_.erase( + std::remove(data_ref_.begin(), data_ref_.end(), data_ref), + data_ref_.end()); + exprtk_debug(("vector_view::remove_ref() - data_ref: %p data_ref_.size(): %d\n", + reinterpret_cast(data_ref), + static_cast(data_ref_.size()))); + } + + bool set_size(const std::size_t new_size) + { + if ((new_size > 0) && (new_size <= base_size_)) + { + size_ = new_size; + exprtk_debug(("vector_view::set_size() - data_: %p size: %lu\n", + reinterpret_cast(data_), + size_)); + return true; + } + + exprtk_debug(("vector_view::set_size() - error invalid new_size: %lu base_size: %lu\n", + new_size, + base_size_)); + return false; + } + + private: + + const std::size_t base_size_; + std::size_t size_; + data_ptr_t data_; + std::vector data_ref_; + }; + + template + inline vector_view make_vector_view(T* data, + const std::size_t size, const std::size_t offset = 0) + { + return vector_view(data + offset, size); + } + + template + inline vector_view make_vector_view(std::vector& v, + const std::size_t size, const std::size_t offset = 0) + { + return vector_view(v.data() + offset, size); + } + + template class results_context; + + template + struct type_store + { + enum store_type + { + e_unknown, + e_scalar , + e_vector , + e_string + }; + + type_store() + : data(0) + , size(0) + , type(e_unknown) + {} + + union + { + void* data; + T* vec_data; + }; + + std::size_t size; + store_type type; + + class parameter_list + { + public: + + explicit parameter_list(std::vector& pl) + : parameter_list_(pl) + {} + + inline bool empty() const + { + return parameter_list_.empty(); + } + + inline std::size_t size() const + { + return parameter_list_.size(); + } + + inline type_store& operator[](const std::size_t& index) + { + return parameter_list_[index]; + } + + inline const type_store& operator[](const std::size_t& index) const + { + return parameter_list_[index]; + } + + inline type_store& front() + { + return parameter_list_[0]; + } + + inline const type_store& front() const + { + return parameter_list_[0]; + } + + inline type_store& back() + { + return parameter_list_.back(); + } + + inline const type_store& back() const + { + return parameter_list_.back(); + } + + private: + + std::vector& parameter_list_; + + friend class results_context; + }; + + template + struct type_view + { + typedef type_store type_store_t; + typedef ViewType value_t; + + explicit type_view(type_store_t& ts) + : ts_(ts) + , data_(reinterpret_cast(ts_.data)) + {} + + explicit type_view(const type_store_t& ts) + : ts_(const_cast(ts)) + , data_(reinterpret_cast(ts_.data)) + {} + + inline std::size_t size() const + { + return ts_.size; + } + + inline value_t& operator[](const std::size_t& i) + { + return data_[i]; + } + + inline const value_t& operator[](const std::size_t& i) const + { + return data_[i]; + } + + inline const value_t* begin() const { return data_; } + inline value_t* begin() { return data_; } + + inline const value_t* end() const + { + return static_cast(data_ + ts_.size); + } + + inline value_t* end() + { + return static_cast(data_ + ts_.size); + } + + type_store_t& ts_; + value_t* data_; + }; + + typedef type_view vector_view; + typedef type_view string_view; + + struct scalar_view + { + typedef type_store type_store_t; + typedef T value_t; + + explicit scalar_view(type_store_t& ts) + : v_(*reinterpret_cast(ts.data)) + {} + + explicit scalar_view(const type_store_t& ts) + : v_(*reinterpret_cast(const_cast(ts).data)) + {} + + inline value_t& operator() () + { + return v_; + } + + inline const value_t& operator() () const + { + return v_; + } + + inline operator value_t() const + { + return v_; + } + + inline operator value_t() + { + return v_; + } + + template + inline bool to_int(IntType& i) const + { + if (!exprtk::details::numeric::is_integer(v_)) + return false; + + i = static_cast(v_); + + return true; + } + + template + inline bool to_uint(UIntType& u) const + { + if (v_ < T(0)) + return false; + else if (!exprtk::details::numeric::is_integer(v_)) + return false; + + u = static_cast(v_); + + return true; + } + + T& v_; + }; + }; + + template + inline std::string to_str(const StringView& view) + { + return std::string(view.begin(),view.size()); + } + + #ifndef exprtk_disable_return_statement + namespace details + { + template class return_node; + template class return_envelope_node; + } + #endif + + template + class results_context + { + public: + + typedef type_store type_store_t; + typedef typename type_store_t::scalar_view scalar_t; + typedef typename type_store_t::vector_view vector_t; + typedef typename type_store_t::string_view string_t; + + results_context() + : results_available_(false) + {} + + inline std::size_t count() const + { + if (results_available_) + return parameter_list_.size(); + else + return 0; + } + + inline type_store_t& operator[](const std::size_t& index) + { + return parameter_list_[index]; + } + + inline const type_store_t& operator[](const std::size_t& index) const + { + return parameter_list_[index]; + } + + inline bool get_scalar(const std::size_t& index, T& out) const + { + if ( + (index < parameter_list_.size()) && + (parameter_list_[index].type == type_store_t::e_scalar) + ) + { + const scalar_t scalar(parameter_list_[index]); + out = scalar(); + return true; + } + + return false; + } + + template + inline bool get_vector(const std::size_t& index, OutputIterator out_itr) const + { + if ( + (index < parameter_list_.size()) && + (parameter_list_[index].type == type_store_t::e_vector) + ) + { + const vector_t vector(parameter_list_[index]); + for (std::size_t i = 0; i < vector.size(); ++i) + { + *(out_itr++) = vector[i]; + } + + return true; + } + + return false; + } + + inline bool get_vector(const std::size_t& index, std::vector& out) const + { + return get_vector(index,std::back_inserter(out)); + } + + inline bool get_string(const std::size_t& index, std::string& out) const + { + if ( + (index < parameter_list_.size()) && + (parameter_list_[index].type == type_store_t::e_string) + ) + { + const string_t str(parameter_list_[index]); + out.assign(str.begin(),str.size()); + return true; + } + + return false; + } + + private: + + inline void clear() + { + results_available_ = false; + } + + typedef std::vector ts_list_t; + typedef typename type_store_t::parameter_list parameter_list_t; + + inline void assign(const parameter_list_t& pl) + { + parameter_list_ = pl.parameter_list_; + results_available_ = true; + } + + bool results_available_; + ts_list_t parameter_list_; + + #ifndef exprtk_disable_return_statement + friend class details::return_node; + friend class details::return_envelope_node; + #endif + }; + + namespace details + { + enum operator_type + { + e_default , e_null , e_add , e_sub , + e_mul , e_div , e_mod , e_pow , + e_atan2 , e_min , e_max , e_avg , + e_sum , e_prod , e_lt , e_lte , + e_eq , e_equal , e_ne , e_nequal , + e_gte , e_gt , e_and , e_nand , + e_or , e_nor , e_xor , e_xnor , + e_mand , e_mor , e_scand , e_scor , + e_shr , e_shl , e_abs , e_acos , + e_acosh , e_asin , e_asinh , e_atan , + e_atanh , e_ceil , e_cos , e_cosh , + e_exp , e_expm1 , e_floor , e_log , + e_log10 , e_log2 , e_log1p , e_logn , + e_neg , e_pos , e_round , e_roundn , + e_root , e_sqrt , e_sin , e_sinc , + e_sinh , e_sec , e_csc , e_tan , + e_tanh , e_cot , e_clamp , e_iclamp , + e_inrange , e_sgn , e_r2d , e_d2r , + e_d2g , e_g2d , e_hypot , e_notl , + e_erf , e_erfc , e_ncdf , e_frac , + e_trunc , e_assign , e_addass , e_subass , + e_mulass , e_divass , e_modass , e_in , + e_like , e_ilike , e_multi , e_smulti , + e_swap , + + // Do not add new functions/operators after this point. + e_sf00 = 1000, e_sf01 = 1001, e_sf02 = 1002, e_sf03 = 1003, + e_sf04 = 1004, e_sf05 = 1005, e_sf06 = 1006, e_sf07 = 1007, + e_sf08 = 1008, e_sf09 = 1009, e_sf10 = 1010, e_sf11 = 1011, + e_sf12 = 1012, e_sf13 = 1013, e_sf14 = 1014, e_sf15 = 1015, + e_sf16 = 1016, e_sf17 = 1017, e_sf18 = 1018, e_sf19 = 1019, + e_sf20 = 1020, e_sf21 = 1021, e_sf22 = 1022, e_sf23 = 1023, + e_sf24 = 1024, e_sf25 = 1025, e_sf26 = 1026, e_sf27 = 1027, + e_sf28 = 1028, e_sf29 = 1029, e_sf30 = 1030, e_sf31 = 1031, + e_sf32 = 1032, e_sf33 = 1033, e_sf34 = 1034, e_sf35 = 1035, + e_sf36 = 1036, e_sf37 = 1037, e_sf38 = 1038, e_sf39 = 1039, + e_sf40 = 1040, e_sf41 = 1041, e_sf42 = 1042, e_sf43 = 1043, + e_sf44 = 1044, e_sf45 = 1045, e_sf46 = 1046, e_sf47 = 1047, + e_sf48 = 1048, e_sf49 = 1049, e_sf50 = 1050, e_sf51 = 1051, + e_sf52 = 1052, e_sf53 = 1053, e_sf54 = 1054, e_sf55 = 1055, + e_sf56 = 1056, e_sf57 = 1057, e_sf58 = 1058, e_sf59 = 1059, + e_sf60 = 1060, e_sf61 = 1061, e_sf62 = 1062, e_sf63 = 1063, + e_sf64 = 1064, e_sf65 = 1065, e_sf66 = 1066, e_sf67 = 1067, + e_sf68 = 1068, e_sf69 = 1069, e_sf70 = 1070, e_sf71 = 1071, + e_sf72 = 1072, e_sf73 = 1073, e_sf74 = 1074, e_sf75 = 1075, + e_sf76 = 1076, e_sf77 = 1077, e_sf78 = 1078, e_sf79 = 1079, + e_sf80 = 1080, e_sf81 = 1081, e_sf82 = 1082, e_sf83 = 1083, + e_sf84 = 1084, e_sf85 = 1085, e_sf86 = 1086, e_sf87 = 1087, + e_sf88 = 1088, e_sf89 = 1089, e_sf90 = 1090, e_sf91 = 1091, + e_sf92 = 1092, e_sf93 = 1093, e_sf94 = 1094, e_sf95 = 1095, + e_sf96 = 1096, e_sf97 = 1097, e_sf98 = 1098, e_sf99 = 1099, + e_sffinal = 1100, + e_sf4ext00 = 2000, e_sf4ext01 = 2001, e_sf4ext02 = 2002, e_sf4ext03 = 2003, + e_sf4ext04 = 2004, e_sf4ext05 = 2005, e_sf4ext06 = 2006, e_sf4ext07 = 2007, + e_sf4ext08 = 2008, e_sf4ext09 = 2009, e_sf4ext10 = 2010, e_sf4ext11 = 2011, + e_sf4ext12 = 2012, e_sf4ext13 = 2013, e_sf4ext14 = 2014, e_sf4ext15 = 2015, + e_sf4ext16 = 2016, e_sf4ext17 = 2017, e_sf4ext18 = 2018, e_sf4ext19 = 2019, + e_sf4ext20 = 2020, e_sf4ext21 = 2021, e_sf4ext22 = 2022, e_sf4ext23 = 2023, + e_sf4ext24 = 2024, e_sf4ext25 = 2025, e_sf4ext26 = 2026, e_sf4ext27 = 2027, + e_sf4ext28 = 2028, e_sf4ext29 = 2029, e_sf4ext30 = 2030, e_sf4ext31 = 2031, + e_sf4ext32 = 2032, e_sf4ext33 = 2033, e_sf4ext34 = 2034, e_sf4ext35 = 2035, + e_sf4ext36 = 2036, e_sf4ext37 = 2037, e_sf4ext38 = 2038, e_sf4ext39 = 2039, + e_sf4ext40 = 2040, e_sf4ext41 = 2041, e_sf4ext42 = 2042, e_sf4ext43 = 2043, + e_sf4ext44 = 2044, e_sf4ext45 = 2045, e_sf4ext46 = 2046, e_sf4ext47 = 2047, + e_sf4ext48 = 2048, e_sf4ext49 = 2049, e_sf4ext50 = 2050, e_sf4ext51 = 2051, + e_sf4ext52 = 2052, e_sf4ext53 = 2053, e_sf4ext54 = 2054, e_sf4ext55 = 2055, + e_sf4ext56 = 2056, e_sf4ext57 = 2057, e_sf4ext58 = 2058, e_sf4ext59 = 2059, + e_sf4ext60 = 2060, e_sf4ext61 = 2061 + }; + + inline std::string to_str(const operator_type opr) + { + switch (opr) + { + case e_add : return "+" ; + case e_sub : return "-" ; + case e_mul : return "*" ; + case e_div : return "/" ; + case e_mod : return "%" ; + case e_pow : return "^" ; + case e_assign : return ":=" ; + case e_addass : return "+=" ; + case e_subass : return "-=" ; + case e_mulass : return "*=" ; + case e_divass : return "/=" ; + case e_modass : return "%=" ; + case e_lt : return "<" ; + case e_lte : return "<=" ; + case e_eq : return "==" ; + case e_equal : return "=" ; + case e_ne : return "!=" ; + case e_nequal : return "<>" ; + case e_gte : return ">=" ; + case e_gt : return ">" ; + case e_and : return "and" ; + case e_or : return "or" ; + case e_xor : return "xor" ; + case e_nand : return "nand"; + case e_nor : return "nor" ; + case e_xnor : return "xnor"; + default : return "N/A" ; + } + } + + struct base_operation_t + { + base_operation_t(const operator_type t, const unsigned int& np) + : type(t) + , num_params(np) + {} + + operator_type type; + unsigned int num_params; + }; + + namespace loop_unroll + { + const unsigned int global_loop_batch_size = + #ifndef exprtk_disable_superscalar_unroll + 16; + #else + 4; + #endif + + struct details + { + explicit details(const std::size_t& vsize, + const unsigned int loop_batch_size = global_loop_batch_size) + : batch_size(loop_batch_size ) + , remainder (vsize % batch_size) + , upper_bound(static_cast(vsize - (remainder ? loop_batch_size : 0))) + {} + + unsigned int batch_size; + int remainder; + int upper_bound; + }; + } + + #ifdef exprtk_enable_debugging + inline void dump_ptr(const std::string& s, const void* ptr, const std::size_t size = 0) + { + if (size) + exprtk_debug(("%s - addr: %p size: %d\n", + s.c_str(), + ptr, + static_cast(size))); + else + exprtk_debug(("%s - addr: %p\n", s.c_str(), ptr)); + } + + template + inline void dump_vector(const std::string& vec_name, const T* data, const std::size_t size) + { + printf("----- %s (%p) -----\n", + vec_name.c_str(), + static_cast(data)); + printf("[ "); + for (std::size_t i = 0; i < size; ++i) + { + printf("%8.3f\t", data[i]); + } + printf(" ]\n"); + printf("---------------------\n"); + } + #else + inline void dump_ptr(const std::string&, const void*) {} + inline void dump_ptr(const std::string&, const void*, const std::size_t) {} + template + inline void dump_vector(const std::string&, const T*, const std::size_t) {} + #endif + + template + class vec_data_store + { + public: + + typedef vec_data_store type; + typedef T* data_t; + + private: + + struct control_block + { + control_block() + : ref_count(1) + , size (0) + , data (0) + , destruct (true) + {} + + explicit control_block(const std::size_t& dsize) + : ref_count(1 ) + , size (dsize) + , data (0 ) + , destruct (true ) + { create_data(); } + + control_block(const std::size_t& dsize, data_t dptr, bool dstrct = false) + : ref_count(1 ) + , size (dsize ) + , data (dptr ) + , destruct (dstrct) + {} + + ~control_block() + { + if (data && destruct && (0 == ref_count)) + { + dump_ptr("~vec_data_store::control_block() data",data); + delete[] data; + data = reinterpret_cast(0); + } + } + + static inline control_block* create(const std::size_t& dsize, data_t data_ptr = data_t(0), bool dstrct = false) + { + if (dsize) + { + if (0 == data_ptr) + return (new control_block(dsize)); + else + return (new control_block(dsize, data_ptr, dstrct)); + } + else + return (new control_block); + } + + static inline void destroy(control_block*& cntrl_blck) + { + if (cntrl_blck) + { + if ( + (0 != cntrl_blck->ref_count) && + (0 == --cntrl_blck->ref_count) + ) + { + delete cntrl_blck; + } + + cntrl_blck = 0; + } + } + + std::size_t ref_count; + std::size_t size; + data_t data; + bool destruct; + + private: + + control_block(const control_block&) exprtk_delete; + control_block& operator=(const control_block&) exprtk_delete; + + inline void create_data() + { + destruct = true; + data = new T[size]; + std::fill_n(data, size, T(0)); + dump_ptr("control_block::create_data() - data", data, size); + } + }; + + public: + + vec_data_store() + : control_block_(control_block::create(0)) + {} + + explicit vec_data_store(const std::size_t& size) + : control_block_(control_block::create(size,reinterpret_cast(0),true)) + {} + + vec_data_store(const std::size_t& size, data_t data, bool dstrct = false) + : control_block_(control_block::create(size, data, dstrct)) + {} + + vec_data_store(const type& vds) + { + control_block_ = vds.control_block_; + control_block_->ref_count++; + } + + ~vec_data_store() + { + control_block::destroy(control_block_); + } + + type& operator=(const type& vds) + { + if (this != &vds) + { + const std::size_t final_size = min_size(control_block_, vds.control_block_); + + vds.control_block_->size = final_size; + control_block_->size = final_size; + + if (control_block_->destruct || (0 == control_block_->data)) + { + control_block::destroy(control_block_); + + control_block_ = vds.control_block_; + control_block_->ref_count++; + } + } + + return (*this); + } + + inline data_t data() + { + return control_block_->data; + } + + inline data_t data() const + { + return control_block_->data; + } + + inline std::size_t size() const + { + return control_block_->size; + } + + inline data_t& ref() + { + return control_block_->data; + } + + inline void dump() const + { + #ifdef exprtk_enable_debugging + exprtk_debug(("size: %d\taddress:%p\tdestruct:%c\n", + size(), + data(), + (control_block_->destruct ? 'T' : 'F'))); + + for (std::size_t i = 0; i < size(); ++i) + { + if (5 == i) + exprtk_debug(("\n")); + + exprtk_debug(("%15.10f ", data()[i])); + } + exprtk_debug(("\n")); + #endif + } + + static inline void match_sizes(type& vds0, type& vds1) + { + const std::size_t size = min_size(vds0.control_block_,vds1.control_block_); + vds0.control_block_->size = size; + vds1.control_block_->size = size; + } + + private: + + static inline std::size_t min_size(const control_block* cb0, const control_block* cb1) + { + const std::size_t size0 = cb0->size; + const std::size_t size1 = cb1->size; + + if (size0 && size1) + return std::min(size0,size1); + else + return (size0) ? size0 : size1; + } + + control_block* control_block_; + }; + + namespace numeric + { + namespace details + { + template + inline T process_impl(const operator_type operation, const T arg) + { + switch (operation) + { + case e_abs : return numeric::abs (arg); + case e_acos : return numeric::acos (arg); + case e_acosh : return numeric::acosh(arg); + case e_asin : return numeric::asin (arg); + case e_asinh : return numeric::asinh(arg); + case e_atan : return numeric::atan (arg); + case e_atanh : return numeric::atanh(arg); + case e_ceil : return numeric::ceil (arg); + case e_cos : return numeric::cos (arg); + case e_cosh : return numeric::cosh (arg); + case e_exp : return numeric::exp (arg); + case e_expm1 : return numeric::expm1(arg); + case e_floor : return numeric::floor(arg); + case e_log : return numeric::log (arg); + case e_log10 : return numeric::log10(arg); + case e_log2 : return numeric::log2 (arg); + case e_log1p : return numeric::log1p(arg); + case e_neg : return numeric::neg (arg); + case e_pos : return numeric::pos (arg); + case e_round : return numeric::round(arg); + case e_sin : return numeric::sin (arg); + case e_sinc : return numeric::sinc (arg); + case e_sinh : return numeric::sinh (arg); + case e_sqrt : return numeric::sqrt (arg); + case e_tan : return numeric::tan (arg); + case e_tanh : return numeric::tanh (arg); + case e_cot : return numeric::cot (arg); + case e_sec : return numeric::sec (arg); + case e_csc : return numeric::csc (arg); + case e_r2d : return numeric::r2d (arg); + case e_d2r : return numeric::d2r (arg); + case e_d2g : return numeric::d2g (arg); + case e_g2d : return numeric::g2d (arg); + case e_notl : return numeric::notl (arg); + case e_sgn : return numeric::sgn (arg); + case e_erf : return numeric::erf (arg); + case e_erfc : return numeric::erfc (arg); + case e_ncdf : return numeric::ncdf (arg); + case e_frac : return numeric::frac (arg); + case e_trunc : return numeric::trunc(arg); + + default : exprtk_debug(("numeric::details::process_impl - Invalid unary operation.\n")); + return std::numeric_limits::quiet_NaN(); + } + } + + template + inline T process_impl(const operator_type operation, const T arg0, const T arg1) + { + switch (operation) + { + case e_add : return (arg0 + arg1); + case e_sub : return (arg0 - arg1); + case e_mul : return (arg0 * arg1); + case e_div : return (arg0 / arg1); + case e_mod : return modulus(arg0,arg1); + case e_pow : return pow(arg0,arg1); + case e_atan2 : return atan2(arg0,arg1); + case e_min : return std::min(arg0,arg1); + case e_max : return std::max(arg0,arg1); + case e_logn : return logn(arg0,arg1); + case e_lt : return (arg0 < arg1) ? T(1) : T(0); + case e_lte : return (arg0 <= arg1) ? T(1) : T(0); + case e_eq : return std::equal_to()(arg0,arg1) ? T(1) : T(0); + case e_ne : return std::not_equal_to()(arg0,arg1) ? T(1) : T(0); + case e_gte : return (arg0 >= arg1) ? T(1) : T(0); + case e_gt : return (arg0 > arg1) ? T(1) : T(0); + case e_and : return and_opr (arg0,arg1); + case e_nand : return nand_opr(arg0,arg1); + case e_or : return or_opr (arg0,arg1); + case e_nor : return nor_opr (arg0,arg1); + case e_xor : return xor_opr (arg0,arg1); + case e_xnor : return xnor_opr(arg0,arg1); + case e_root : return root (arg0,arg1); + case e_roundn : return roundn (arg0,arg1); + case e_equal : return equal (arg0,arg1); + case e_nequal : return nequal (arg0,arg1); + case e_hypot : return hypot (arg0,arg1); + case e_shr : return shr (arg0,arg1); + case e_shl : return shl (arg0,arg1); + + default : exprtk_debug(("numeric::details::process_impl - Invalid binary operation.\n")); + return std::numeric_limits::quiet_NaN(); + } + } + + template + inline T process_impl(const operator_type operation, const T arg0, const T arg1, int_type_tag) + { + switch (operation) + { + case e_add : return (arg0 + arg1); + case e_sub : return (arg0 - arg1); + case e_mul : return (arg0 * arg1); + case e_div : return (arg0 / arg1); + case e_mod : return arg0 % arg1; + case e_pow : return pow(arg0,arg1); + case e_min : return std::min(arg0,arg1); + case e_max : return std::max(arg0,arg1); + case e_logn : return logn(arg0,arg1); + case e_lt : return (arg0 < arg1) ? T(1) : T(0); + case e_lte : return (arg0 <= arg1) ? T(1) : T(0); + case e_eq : return (arg0 == arg1) ? T(1) : T(0); + case e_ne : return (arg0 != arg1) ? T(1) : T(0); + case e_gte : return (arg0 >= arg1) ? T(1) : T(0); + case e_gt : return (arg0 > arg1) ? T(1) : T(0); + case e_and : return ((arg0 != T(0)) && (arg1 != T(0))) ? T(1) : T(0); + case e_nand : return ((arg0 != T(0)) && (arg1 != T(0))) ? T(0) : T(1); + case e_or : return ((arg0 != T(0)) || (arg1 != T(0))) ? T(1) : T(0); + case e_nor : return ((arg0 != T(0)) || (arg1 != T(0))) ? T(0) : T(1); + case e_xor : return arg0 ^ arg1; + case e_xnor : return !(arg0 ^ arg1); + case e_root : return root(arg0,arg1); + case e_equal : return arg0 == arg1; + case e_nequal : return arg0 != arg1; + case e_hypot : return hypot(arg0,arg1); + case e_shr : return arg0 >> arg1; + case e_shl : return arg0 << arg1; + + default : exprtk_debug(("numeric::details::process_impl - Invalid binary operation.\n")); + return std::numeric_limits::quiet_NaN(); + } + } + } + + template + inline T process(const operator_type operation, const T arg) + { + return exprtk::details::numeric::details::process_impl(operation,arg); + } + + template + inline T process(const operator_type operation, const T arg0, const T arg1) + { + return exprtk::details::numeric::details::process_impl(operation, arg0, arg1); + } + } + + template + struct node_collector_interface + { + typedef Node* node_ptr_t; + typedef Node** node_pp_t; + typedef std::vector noderef_list_t; + + virtual ~node_collector_interface() + {} + + virtual void collect_nodes(noderef_list_t&) + {} + }; + + template + struct node_depth_base; + + template + class expression_node : public node_collector_interface > + , public node_depth_base > + { + public: + + enum node_type + { + e_none , e_null , e_constant , e_unary , + e_binary , e_binary_ext , e_trinary , e_quaternary , + e_vararg , e_conditional , e_while , e_repeat , + e_for , e_switch , e_mswitch , e_return , + e_retenv , e_variable , e_stringvar , e_stringconst , + e_stringvarrng , e_cstringvarrng , e_strgenrange , e_strconcat , + e_stringvarsize , e_strswap , e_stringsize , e_stringvararg , + e_function , e_vafunction , e_genfunction , e_strfunction , + e_strcondition , e_strccondition , e_add , e_sub , + e_mul , e_div , e_mod , e_pow , + e_lt , e_lte , e_gt , e_gte , + e_eq , e_ne , e_and , e_nand , + e_or , e_nor , e_xor , e_xnor , + e_in , e_like , e_ilike , e_inranges , + e_ipow , e_ipowinv , e_abs , e_acos , + e_acosh , e_asin , e_asinh , e_atan , + e_atanh , e_ceil , e_cos , e_cosh , + e_exp , e_expm1 , e_floor , e_log , + e_log10 , e_log2 , e_log1p , e_neg , + e_pos , e_round , e_sin , e_sinc , + e_sinh , e_sqrt , e_tan , e_tanh , + e_cot , e_sec , e_csc , e_r2d , + e_d2r , e_d2g , e_g2d , e_notl , + e_sgn , e_erf , e_erfc , e_ncdf , + e_frac , e_trunc , e_uvouv , e_vov , + e_cov , e_voc , e_vob , e_bov , + e_cob , e_boc , e_vovov , e_vovoc , + e_vocov , e_covov , e_covoc , e_vovovov , + e_vovovoc , e_vovocov , e_vocovov , e_covovov , + e_covocov , e_vocovoc , e_covovoc , e_vococov , + e_sf3ext , e_sf4ext , e_nulleq , e_strass , + e_vector , e_vecsize , e_vecelem , e_veccelem , + e_vecelemrtc , e_veccelemrtc , e_rbvecelem , e_rbvecelemrtc , + e_rbveccelem , e_rbveccelemrtc , e_vecinit , e_vecvalass , + e_vecvecass , e_vecopvalass , e_vecopvecass , e_vecfunc , + e_vecvecswap , e_vecvecineq , e_vecvalineq , e_valvecineq , + e_vecvecarith , e_vecvalarith , e_valvecarith , e_vecunaryop , + e_vecondition , e_break , e_continue , e_swap , + e_assert + }; + + typedef T value_type; + typedef expression_node* expression_ptr; + typedef node_collector_interface > nci_t; + typedef typename nci_t::noderef_list_t noderef_list_t; + typedef node_depth_base > ndb_t; + + virtual ~expression_node() + {} + + inline virtual T value() const + { + return std::numeric_limits::quiet_NaN(); + } + + inline virtual expression_node* branch(const std::size_t& index = 0) const + { + return reinterpret_cast(index * 0); + } + + inline virtual node_type type() const + { + return e_none; + } + + inline virtual bool valid() const + { + return true; + } + }; // class expression_node + + template + inline bool is_generally_string_node(const expression_node* node); + + inline bool is_true(const double v) + { + return std::not_equal_to()(0.0,v); + } + + inline bool is_true(const long double v) + { + return std::not_equal_to()(0.0L,v); + } + + inline bool is_true(const float v) + { + return std::not_equal_to()(0.0f,v); + } + + template + inline bool is_true(const expression_node* node) + { + return std::not_equal_to()(T(0),node->value()); + } + + template + inline bool is_true(const std::pair*,bool>& node) + { + return std::not_equal_to()(T(0),node.first->value()); + } + + template + inline bool is_false(const expression_node* node) + { + return std::equal_to()(T(0),node->value()); + } + + template + inline bool is_false(const std::pair*,bool>& node) + { + return std::equal_to()(T(0),node.first->value()); + } + + template + inline bool is_literal_node(const expression_node* node) + { + return node && (details::expression_node::e_constant == node->type()); + } + + template + inline bool is_unary_node(const expression_node* node) + { + return node && (details::expression_node::e_unary == node->type()); + } + + template + inline bool is_neg_unary_node(const expression_node* node) + { + return node && (details::expression_node::e_neg == node->type()); + } + + template + inline bool is_binary_node(const expression_node* node) + { + return node && (details::expression_node::e_binary == node->type()); + } + + template + inline bool is_variable_node(const expression_node* node) + { + return node && (details::expression_node::e_variable == node->type()); + } + + template + inline bool is_ivariable_node(const expression_node* node) + { + return node && + ( + details::expression_node::e_variable == node->type() || + details::expression_node::e_vecelem == node->type() || + details::expression_node::e_veccelem == node->type() || + details::expression_node::e_vecelemrtc == node->type() || + details::expression_node::e_veccelemrtc == node->type() || + details::expression_node::e_rbvecelem == node->type() || + details::expression_node::e_rbveccelem == node->type() || + details::expression_node::e_rbvecelemrtc == node->type() || + details::expression_node::e_rbveccelemrtc == node->type() + ); + } + + template + inline bool is_vector_elem_node(const expression_node* node) + { + return node && (details::expression_node::e_vecelem == node->type()); + } + + template + inline bool is_vector_celem_node(const expression_node* node) + { + return node && (details::expression_node::e_veccelem == node->type()); + } + + template + inline bool is_vector_elem_rtc_node(const expression_node* node) + { + return node && (details::expression_node::e_vecelemrtc == node->type()); + } + + template + inline bool is_vector_celem_rtc_node(const expression_node* node) + { + return node && (details::expression_node::e_veccelemrtc == node->type()); + } + + template + inline bool is_rebasevector_elem_node(const expression_node* node) + { + return node && (details::expression_node::e_rbvecelem == node->type()); + } + + template + inline bool is_rebasevector_elem_rtc_node(const expression_node* node) + { + return node && (details::expression_node::e_rbvecelemrtc == node->type()); + } + + template + inline bool is_rebasevector_celem_rtc_node(const expression_node* node) + { + return node && (details::expression_node::e_rbveccelemrtc == node->type()); + } + + template + inline bool is_rebasevector_celem_node(const expression_node* node) + { + return node && (details::expression_node::e_rbveccelem == node->type()); + } + + template + inline bool is_vector_node(const expression_node* node) + { + return node && (details::expression_node::e_vector == node->type()); + } + + template + inline bool is_ivector_node(const expression_node* node) + { + if (node) + { + switch (node->type()) + { + case details::expression_node::e_vector : + case details::expression_node::e_vecvalass : + case details::expression_node::e_vecvecass : + case details::expression_node::e_vecopvalass : + case details::expression_node::e_vecopvecass : + case details::expression_node::e_vecvecswap : + case details::expression_node::e_vecvecarith : + case details::expression_node::e_vecvalarith : + case details::expression_node::e_valvecarith : + case details::expression_node::e_vecunaryop : + case details::expression_node::e_vecondition : return true; + default : return false; + } + } + else + return false; + } + + template + inline bool is_constant_node(const expression_node* node) + { + return node && + ( + details::expression_node::e_constant == node->type() || + details::expression_node::e_stringconst == node->type() + ); + } + + template + inline bool is_null_node(const expression_node* node) + { + return node && (details::expression_node::e_null == node->type()); + } + + template + inline bool is_break_node(const expression_node* node) + { + return node && (details::expression_node::e_break == node->type()); + } + + template + inline bool is_continue_node(const expression_node* node) + { + return node && (details::expression_node::e_continue == node->type()); + } + + template + inline bool is_swap_node(const expression_node* node) + { + return node && (details::expression_node::e_swap == node->type()); + } + + template + inline bool is_function(const expression_node* node) + { + return node && (details::expression_node::e_function == node->type()); + } + + template + inline bool is_vararg_node(const expression_node* node) + { + return node && (details::expression_node::e_vararg == node->type()); + } + + template + inline bool is_return_node(const expression_node* node) + { + return node && (details::expression_node::e_return == node->type()); + } + + template class unary_node; + + template + inline bool is_negate_node(const expression_node* node) + { + if (node && is_unary_node(node)) + { + return (details::e_neg == static_cast*>(node)->operation()); + } + else + return false; + } + + template + inline bool is_assert_node(const expression_node* node) + { + return node && (details::expression_node::e_assert == node->type()); + } + + template + inline bool branch_deletable(const expression_node* node) + { + return (0 != node) && + !is_variable_node(node) && + !is_string_node (node) ; + } + + template + inline bool all_nodes_valid(expression_node* const (&b)[N]) + { + for (std::size_t i = 0; i < N; ++i) + { + if (0 == b[i]) return false; + } + + return true; + } + + template class Sequence> + inline bool all_nodes_valid(const Sequence*,Allocator>& b) + { + for (std::size_t i = 0; i < b.size(); ++i) + { + if (0 == b[i]) return false; + } + + return true; + } + + template + inline bool all_nodes_variables(expression_node* const (&b)[N]) + { + for (std::size_t i = 0; i < N; ++i) + { + if (0 == b[i]) + return false; + else if (!is_variable_node(b[i])) + return false; + } + + return true; + } + + template class Sequence> + inline bool all_nodes_variables(const Sequence*,Allocator>& b) + { + for (std::size_t i = 0; i < b.size(); ++i) + { + if (0 == b[i]) + return false; + else if (!is_variable_node(b[i])) + return false; + } + + return true; + } + + template + class node_collection_destructor + { + public: + + typedef node_collector_interface nci_t; + + typedef typename nci_t::node_ptr_t node_ptr_t; + typedef typename nci_t::node_pp_t node_pp_t; + typedef typename nci_t::noderef_list_t noderef_list_t; + + static void delete_nodes(node_ptr_t& root) + { + std::vector node_delete_list; + node_delete_list.reserve(1000); + + collect_nodes(root, node_delete_list); + + for (std::size_t i = 0; i < node_delete_list.size(); ++i) + { + node_ptr_t& node = *node_delete_list[i]; + exprtk_debug(("ncd::delete_nodes() - deleting: %p\n", reinterpret_cast(node))); + delete node; + node = reinterpret_cast(0); + } + } + + private: + + static void collect_nodes(node_ptr_t& root, noderef_list_t& node_delete_list) + { + std::deque node_list; + node_list.push_back(root); + node_delete_list.push_back(&root); + + noderef_list_t child_node_delete_list; + child_node_delete_list.reserve(1000); + + while (!node_list.empty()) + { + node_list.front()->collect_nodes(child_node_delete_list); + + if (!child_node_delete_list.empty()) + { + for (std::size_t i = 0; i < child_node_delete_list.size(); ++i) + { + node_pp_t& node = child_node_delete_list[i]; + + if (0 == (*node)) + { + exprtk_debug(("ncd::collect_nodes() - null node encountered.\n")); + } + + node_list.push_back(*node); + } + + node_delete_list.insert( + node_delete_list.end(), + child_node_delete_list.begin(), child_node_delete_list.end()); + + child_node_delete_list.clear(); + } + + node_list.pop_front(); + } + + std::reverse(node_delete_list.begin(), node_delete_list.end()); + } + }; + + template + inline void free_all_nodes(NodeAllocator& node_allocator, expression_node* (&b)[N]) + { + for (std::size_t i = 0; i < N; ++i) + { + free_node(node_allocator,b[i]); + } + } + + template class Sequence> + inline void free_all_nodes(NodeAllocator& node_allocator, Sequence*,Allocator>& b) + { + for (std::size_t i = 0; i < b.size(); ++i) + { + free_node(node_allocator,b[i]); + } + + b.clear(); + } + + template + inline void free_node(NodeAllocator&, expression_node*& node) + { + if ((0 == node) || is_variable_node(node) || is_string_node(node)) + { + return; + } + + node_collection_destructor > + ::delete_nodes(node); + } + + template + inline void destroy_node(expression_node*& node) + { + if (0 != node) + { + node_collection_destructor > + ::delete_nodes(node); + } + } + + template + struct node_depth_base + { + typedef Node* node_ptr_t; + typedef std::pair nb_pair_t; + + node_depth_base() + : depth_set(false) + , depth(0) + {} + + virtual ~node_depth_base() + {} + + virtual std::size_t node_depth() const { return 1; } + + std::size_t compute_node_depth(const Node* const& node) const + { + if (!depth_set) + { + depth = 1 + (node ? node->node_depth() : 0); + depth_set = true; + } + + return depth; + } + + std::size_t compute_node_depth(const nb_pair_t& branch) const + { + if (!depth_set) + { + depth = 1 + (branch.first ? branch.first->node_depth() : 0); + depth_set = true; + } + + return depth; + } + + template + std::size_t compute_node_depth(const nb_pair_t (&branch)[N]) const + { + if (!depth_set) + { + depth = 0; + + for (std::size_t i = 0; i < N; ++i) + { + if (branch[i].first) + { + depth = std::max(depth,branch[i].first->node_depth()); + } + } + + depth += 1; + depth_set = true; + } + + return depth; + } + + template + std::size_t max_node_depth(const BranchType& n0, const BranchType& n1) const + { + return std::max(compute_node_depth(n0), compute_node_depth(n1)); + } + + template + std::size_t max_node_depth(const BranchType& n0, const BranchType& n1, const BranchType& n2) const + { + return std::max(compute_node_depth(n0), + std::max(compute_node_depth(n1), compute_node_depth(n2))); + } + + template + std::size_t max_node_depth(const BranchType& n0, const BranchType& n1, + const BranchType& n2, const BranchType& n3) const + { + return std::max( + std::max(compute_node_depth(n0), compute_node_depth(n1)), + std::max(compute_node_depth(n2), compute_node_depth(n3))); + } + + template + std::size_t compute_node_depth(const BranchType& n0, const BranchType& n1) const + { + if (!depth_set) + { + depth = 1 + max_node_depth(n0, n1); + depth_set = true; + } + + return depth; + } + + template + std::size_t compute_node_depth(const BranchType& n0, const BranchType& n1, + const BranchType& n2) const + { + if (!depth_set) + { + depth = 1 + max_node_depth(n0, n1, n2); + depth_set = true; + } + + return depth; + } + + template + std::size_t compute_node_depth(const BranchType& n0, const BranchType& n1, + const BranchType& n2, const BranchType& n3) const + { + if (!depth_set) + { + depth = 1 + max_node_depth(n0, n1, n2, n3); + depth_set = true; + } + + return depth; + } + + template class Sequence> + std::size_t compute_node_depth(const Sequence& branch_list) const + { + if (!depth_set) + { + for (std::size_t i = 0; i < branch_list.size(); ++i) + { + if (branch_list[i]) + { + depth = std::max(depth, compute_node_depth(branch_list[i])); + } + } + + depth_set = true; + } + + return depth; + } + + template class Sequence> + std::size_t compute_node_depth(const Sequence& branch_list) const + { + if (!depth_set) + { + for (std::size_t i = 0; i < branch_list.size(); ++i) + { + if (branch_list[i].first) + { + depth = std::max(depth, compute_node_depth(branch_list[i].first)); + } + } + + depth_set = true; + } + + return depth; + } + + mutable bool depth_set; + mutable std::size_t depth; + + template + void collect(node_ptr_t const& node, + const bool deletable, + NodeSequence& delete_node_list) const + { + if ((0 != node) && deletable) + { + delete_node_list.push_back(const_cast(&node)); + } + } + + template + void collect(const nb_pair_t& branch, + NodeSequence& delete_node_list) const + { + collect(branch.first, branch.second, delete_node_list); + } + + template + void collect(Node*& node, + NodeSequence& delete_node_list) const + { + collect(node, branch_deletable(node), delete_node_list); + } + + template + void collect(const nb_pair_t(&branch)[N], + NodeSequence& delete_node_list) const + { + for (std::size_t i = 0; i < N; ++i) + { + collect(branch[i].first, branch[i].second, delete_node_list); + } + } + + template class Sequence, + typename NodeSequence> + void collect(const Sequence& branch, + NodeSequence& delete_node_list) const + { + for (std::size_t i = 0; i < branch.size(); ++i) + { + collect(branch[i].first, branch[i].second, delete_node_list); + } + } + + template class Sequence, + typename NodeSequence> + void collect(const Sequence& branch_list, + NodeSequence& delete_node_list) const + { + for (std::size_t i = 0; i < branch_list.size(); ++i) + { + collect(branch_list[i], branch_deletable(branch_list[i]), delete_node_list); + } + } + + template class Sequence, + typename NodeSequence> + void collect(const Sequence& branch_list, + const Sequence& branch_deletable_list, + NodeSequence& delete_node_list) const + { + for (std::size_t i = 0; i < branch_list.size(); ++i) + { + collect(branch_list[i], branch_deletable_list[i], delete_node_list); + } + } + }; + + template + class vector_holder + { + private: + + typedef Type value_type; + typedef value_type* value_ptr; + typedef const value_ptr const_value_ptr; + typedef vector_holder vector_holder_t; + + class vector_holder_base + { + public: + + virtual ~vector_holder_base() + {} + + inline value_ptr operator[](const std::size_t& index) const + { + return value_at(index); + } + + inline std::size_t size() const + { + return vector_size(); + } + + inline std::size_t base_size() const + { + return vector_base_size(); + } + + inline value_ptr data() const + { + return value_at(0); + } + + virtual inline bool rebaseable() const + { + return false; + } + + virtual void set_ref(value_ptr*) + {} + + virtual void remove_ref(value_ptr*) + {} + + virtual vector_view* rebaseable_instance() + { + return reinterpret_cast*>(0); + } + + protected: + + virtual value_ptr value_at(const std::size_t&) const = 0; + virtual std::size_t vector_size() const = 0; + virtual std::size_t vector_base_size() const = 0; + }; + + class array_vector_impl exprtk_final : public vector_holder_base + { + public: + + array_vector_impl(const Type* vec, const std::size_t& vec_size) + : vec_(vec) + , size_(vec_size) + {} + + protected: + + value_ptr value_at(const std::size_t& index) const exprtk_override + { + assert(index < size_); + return const_cast(vec_ + index); + } + + std::size_t vector_size() const exprtk_override + { + return size_; + } + + std::size_t vector_base_size() const exprtk_override + { + return vector_size(); + } + + private: + + array_vector_impl(const array_vector_impl&) exprtk_delete; + array_vector_impl& operator=(const array_vector_impl&) exprtk_delete; + + const Type* vec_; + const std::size_t size_; + }; + + template class Sequence> + class sequence_vector_impl exprtk_final : public vector_holder_base + { + public: + + typedef Sequence sequence_t; + + explicit sequence_vector_impl(sequence_t& seq) + : sequence_(seq) + {} + + protected: + + value_ptr value_at(const std::size_t& index) const exprtk_override + { + assert(index < sequence_.size()); + return (&sequence_[index]); + } + + std::size_t vector_size() const exprtk_override + { + return sequence_.size(); + } + + std::size_t vector_base_size() const exprtk_override + { + return vector_size(); + } + + private: + + sequence_vector_impl(const sequence_vector_impl&) exprtk_delete; + sequence_vector_impl& operator=(const sequence_vector_impl&) exprtk_delete; + + sequence_t& sequence_; + }; + + class vector_view_impl exprtk_final : public vector_holder_base + { + public: + + typedef exprtk::vector_view vector_view_t; + + explicit vector_view_impl(vector_view_t& vec_view) + : vec_view_(vec_view) + { + assert(vec_view_.size() > 0); + } + + void set_ref(value_ptr* ref) exprtk_override + { + vec_view_.set_ref(ref); + } + + void remove_ref(value_ptr* ref) exprtk_override + { + vec_view_.remove_ref(ref); + } + + bool rebaseable() const exprtk_override + { + return true; + } + + vector_view* rebaseable_instance() exprtk_override + { + return &vec_view_; + } + + protected: + + value_ptr value_at(const std::size_t& index) const exprtk_override + { + assert(index < vec_view_.size()); + return (&vec_view_[index]); + } + + std::size_t vector_size() const exprtk_override + { + return vec_view_.size(); + } + + std::size_t vector_base_size() const exprtk_override + { + return vec_view_.base_size(); + } + + private: + + vector_view_impl(const vector_view_impl&) exprtk_delete; + vector_view_impl& operator=(const vector_view_impl&) exprtk_delete; + + vector_view_t& vec_view_; + }; + + class resizable_vector_impl exprtk_final : public vector_holder_base + { + public: + + resizable_vector_impl(vector_holder& vec_view_holder, + const Type* vec, + const std::size_t& vec_size) + : vec_(vec) + , size_(vec_size) + , vec_view_holder_(*vec_view_holder.rebaseable_instance()) + { + assert(vec_view_holder.rebaseable_instance()); + assert(size_ <= vector_base_size()); + } + + virtual ~resizable_vector_impl() exprtk_override + {} + + protected: + + value_ptr value_at(const std::size_t& index) const exprtk_override + { + assert(index < vector_size()); + return const_cast(vec_ + index); + } + + std::size_t vector_size() const exprtk_override + { + return vec_view_holder_.size(); + } + + std::size_t vector_base_size() const exprtk_override + { + return vec_view_holder_.base_size(); + } + + bool rebaseable() const exprtk_override + { + return true; + } + + virtual vector_view* rebaseable_instance() exprtk_override + { + return &vec_view_holder_; + } + + private: + + resizable_vector_impl(const resizable_vector_impl&) exprtk_delete; + resizable_vector_impl& operator=(const resizable_vector_impl&) exprtk_delete; + + const Type* vec_; + const std::size_t size_; + vector_view& vec_view_holder_; + }; + + public: + + typedef typename details::vec_data_store vds_t; + + vector_holder(Type* vec, const std::size_t& vec_size) + : vector_holder_base_(new(buffer)array_vector_impl(vec,vec_size)) + {} + + explicit vector_holder(const vds_t& vds) + : vector_holder_base_(new(buffer)array_vector_impl(vds.data(),vds.size())) + {} + + template + explicit vector_holder(std::vector& vec) + : vector_holder_base_(new(buffer)sequence_vector_impl(vec)) + {} + + explicit vector_holder(exprtk::vector_view& vec) + : vector_holder_base_(new(buffer)vector_view_impl(vec)) + {} + + explicit vector_holder(vector_holder_t& vec_holder, const vds_t& vds) + : vector_holder_base_(new(buffer)resizable_vector_impl(vec_holder, vds.data(), vds.size())) + {} + + inline value_ptr operator[](const std::size_t& index) const + { + return (*vector_holder_base_)[index]; + } + + inline std::size_t size() const + { + return vector_holder_base_->size(); + } + + inline std::size_t base_size() const + { + return vector_holder_base_->base_size(); + } + + inline value_ptr data() const + { + return vector_holder_base_->data(); + } + + void set_ref(value_ptr* ref) + { + if (rebaseable()) + { + vector_holder_base_->set_ref(ref); + } + } + + void remove_ref(value_ptr* ref) + { + if (rebaseable()) + { + vector_holder_base_->remove_ref(ref); + } + } + + bool rebaseable() const + { + return vector_holder_base_->rebaseable(); + } + + vector_view* rebaseable_instance() + { + return vector_holder_base_->rebaseable_instance(); + } + + private: + + vector_holder(const vector_holder&) exprtk_delete; + vector_holder& operator=(const vector_holder&) exprtk_delete; + + mutable vector_holder_base* vector_holder_base_; + uchar_t buffer[64]; + }; + + template + class null_node exprtk_final : public expression_node + { + public: + + inline T value() const exprtk_override + { + return std::numeric_limits::quiet_NaN(); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_null; + } + }; + + template + inline void construct_branch_pair(std::pair*,bool> (&branch)[N], + expression_node* b, + const std::size_t& index) + { + if (b && (index < N)) + { + branch[index] = std::make_pair(b,branch_deletable(b)); + } + } + + template + inline void construct_branch_pair(std::pair*,bool>& branch, expression_node* b) + { + if (b) + { + branch = std::make_pair(b,branch_deletable(b)); + } + } + + template + inline void init_branches(std::pair*,bool> (&branch)[N], + expression_node* b0, + expression_node* b1 = reinterpret_cast*>(0), + expression_node* b2 = reinterpret_cast*>(0), + expression_node* b3 = reinterpret_cast*>(0), + expression_node* b4 = reinterpret_cast*>(0), + expression_node* b5 = reinterpret_cast*>(0), + expression_node* b6 = reinterpret_cast*>(0), + expression_node* b7 = reinterpret_cast*>(0), + expression_node* b8 = reinterpret_cast*>(0), + expression_node* b9 = reinterpret_cast*>(0)) + { + construct_branch_pair(branch, b0, 0); + construct_branch_pair(branch, b1, 1); + construct_branch_pair(branch, b2, 2); + construct_branch_pair(branch, b3, 3); + construct_branch_pair(branch, b4, 4); + construct_branch_pair(branch, b5, 5); + construct_branch_pair(branch, b6, 6); + construct_branch_pair(branch, b7, 7); + construct_branch_pair(branch, b8, 8); + construct_branch_pair(branch, b9, 9); + } + + template + class null_eq_node exprtk_final : public expression_node + { + public: + + typedef expression_node* expression_ptr; + typedef std::pair branch_t; + + explicit null_eq_node(expression_ptr branch, const bool equality = true) + : equality_(equality) + { + construct_branch_pair(branch_, branch); + assert(valid()); + } + + inline T value() const exprtk_override + { + const T v = branch_.first->value(); + const bool result = details::numeric::is_nan(v); + + if (result) + return equality_ ? T(1) : T(0); + else + return equality_ ? T(0) : T(1); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_nulleq; + } + + inline expression_node* branch(const std::size_t&) const exprtk_override + { + return branch_.first; + } + + inline bool valid() const exprtk_override + { + return branch_.first; + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(branch_, node_delete_list); + } + + std::size_t node_depth() const exprtk_override + { + return expression_node::ndb_t::compute_node_depth(branch_); + } + + private: + + bool equality_; + branch_t branch_; + }; + + template + class literal_node exprtk_final : public expression_node + { + public: + + explicit literal_node(const T& v) + : value_(v) + {} + + inline T value() const exprtk_override + { + return value_; + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_constant; + } + + inline expression_node* branch(const std::size_t&) const exprtk_override + { + return reinterpret_cast*>(0); + } + + private: + + literal_node(const literal_node&) exprtk_delete; + literal_node& operator=(const literal_node&) exprtk_delete; + + const T value_; + }; + + template + struct range_pack; + + template + struct range_data_type; + + template + class range_interface + { + public: + + typedef range_pack range_t; + + virtual ~range_interface() + {} + + virtual range_t& range_ref() = 0; + + virtual const range_t& range_ref() const = 0; + }; + + #ifndef exprtk_disable_string_capabilities + template + class string_base_node + { + public: + + typedef range_data_type range_data_type_t; + + virtual ~string_base_node() + {} + + virtual std::string str () const = 0; + + virtual char_cptr base() const = 0; + + virtual std::size_t size() const = 0; + }; + + template + class string_literal_node exprtk_final + : public expression_node + , public string_base_node + , public range_interface + { + public: + + typedef range_pack range_t; + + explicit string_literal_node(const std::string& v) + : value_(v) + { + rp_.n0_c = std::make_pair(true, 0); + rp_.n1_c = std::make_pair(true, v.size()); + rp_.cache.first = rp_.n0_c.second; + rp_.cache.second = rp_.n1_c.second; + } + + inline T value() const exprtk_override + { + return std::numeric_limits::quiet_NaN(); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_stringconst; + } + + inline expression_node* branch(const std::size_t&) const exprtk_override + { + return reinterpret_cast*>(0); + } + + std::string str() const exprtk_override + { + return value_; + } + + char_cptr base() const exprtk_override + { + return value_.data(); + } + + std::size_t size() const exprtk_override + { + return value_.size(); + } + + range_t& range_ref() exprtk_override + { + return rp_; + } + + const range_t& range_ref() const exprtk_override + { + return rp_; + } + + private: + + string_literal_node(const string_literal_node&) exprtk_delete; + string_literal_node& operator=(const string_literal_node&) exprtk_delete; + + const std::string value_; + range_t rp_; + }; + #endif + + template + class unary_node : public expression_node + { + public: + + typedef expression_node* expression_ptr; + typedef std::pair branch_t; + + unary_node(const operator_type& opr, expression_ptr branch) + : operation_(opr) + { + construct_branch_pair(branch_,branch); + assert(valid()); + } + + inline T value() const exprtk_override + { + return numeric::process + (operation_,branch_.first->value()); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_unary; + } + + inline operator_type operation() + { + return operation_; + } + + inline expression_node* branch(const std::size_t&) const exprtk_override + { + return branch_.first; + } + + inline bool valid() const exprtk_override + { + return branch_.first && branch_.first->valid(); + } + + inline void release() + { + branch_.second = false; + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(branch_, node_delete_list); + } + + std::size_t node_depth() const exprtk_final + { + return expression_node::ndb_t::compute_node_depth(branch_); + } + + private: + + operator_type operation_; + branch_t branch_; + }; + + template + class binary_node : public expression_node + { + public: + + typedef expression_node* expression_ptr; + typedef std::pair branch_t; + + binary_node(const operator_type& opr, + expression_ptr branch0, + expression_ptr branch1) + : operation_(opr) + { + init_branches<2>(branch_, branch0, branch1); + assert(valid()); + } + + inline T value() const exprtk_override + { + return numeric::process + ( + operation_, + branch_[0].first->value(), + branch_[1].first->value() + ); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_binary; + } + + inline operator_type operation() + { + return operation_; + } + + inline expression_node* branch(const std::size_t& index = 0) const exprtk_override + { + assert(index < 2); + return branch_[index].first; + } + + inline bool valid() const exprtk_override + { + return + branch_[0].first && branch_[0].first->valid() && + branch_[1].first && branch_[1].first->valid() ; + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(branch_, node_delete_list); + } + + std::size_t node_depth() const exprtk_final + { + return expression_node::ndb_t::template compute_node_depth<2>(branch_); + } + + private: + + operator_type operation_; + branch_t branch_[2]; + }; + + template + class binary_ext_node exprtk_final : public expression_node + { + public: + + typedef expression_node* expression_ptr; + typedef std::pair branch_t; + + binary_ext_node(expression_ptr branch0, expression_ptr branch1) + { + init_branches<2>(branch_, branch0, branch1); + assert(valid()); + } + + inline T value() const exprtk_override + { + const T arg0 = branch_[0].first->value(); + const T arg1 = branch_[1].first->value(); + return Operation::process(arg0,arg1); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_binary_ext; + } + + inline operator_type operation() + { + return Operation::operation(); + } + + inline expression_node* branch(const std::size_t& index = 0) const exprtk_override + { + assert(index < 2); + return branch_[index].first; + } + + inline bool valid() const exprtk_override + { + return + branch_[0].first && branch_[0].first->valid() && + branch_[1].first && branch_[1].first->valid() ; + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(branch_, node_delete_list); + } + + std::size_t node_depth() const exprtk_override + { + return expression_node::ndb_t::template compute_node_depth<2>(branch_); + } + + protected: + + branch_t branch_[2]; + }; + + template + class trinary_node : public expression_node + { + public: + + typedef expression_node* expression_ptr; + typedef std::pair branch_t; + + trinary_node(const operator_type& opr, + expression_ptr branch0, + expression_ptr branch1, + expression_ptr branch2) + : operation_(opr) + { + init_branches<3>(branch_, branch0, branch1, branch2); + assert(valid()); + } + + inline T value() const exprtk_override + { + const T arg0 = branch_[0].first->value(); + const T arg1 = branch_[1].first->value(); + const T arg2 = branch_[2].first->value(); + + switch (operation_) + { + case e_inrange : return (arg1 < arg0) ? T(0) : ((arg1 > arg2) ? T(0) : T(1)); + + case e_clamp : return (arg1 < arg0) ? arg0 : (arg1 > arg2 ? arg2 : arg1); + + case e_iclamp : if ((arg1 <= arg0) || (arg1 >= arg2)) + return arg1; + else + return ((T(2) * arg1 <= (arg2 + arg0)) ? arg0 : arg2); + + default : exprtk_debug(("trinary_node::value() - Error: Invalid operation\n")); + return std::numeric_limits::quiet_NaN(); + } + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_trinary; + } + + inline bool valid() const exprtk_override + { + return + branch_[0].first && branch_[0].first->valid() && + branch_[1].first && branch_[1].first->valid() && + branch_[2].first && branch_[2].first->valid() ; + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(branch_, node_delete_list); + } + + std::size_t node_depth() const exprtk_override exprtk_final + { + return expression_node::ndb_t::template compute_node_depth<3>(branch_); + } + + protected: + + operator_type operation_; + branch_t branch_[3]; + }; + + template + class quaternary_node : public expression_node + { + public: + + typedef expression_node* expression_ptr; + typedef std::pair branch_t; + + quaternary_node(const operator_type& opr, + expression_ptr branch0, + expression_ptr branch1, + expression_ptr branch2, + expression_ptr branch3) + : operation_(opr) + { + init_branches<4>(branch_, branch0, branch1, branch2, branch3); + } + + inline T value() const exprtk_override + { + return std::numeric_limits::quiet_NaN(); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_quaternary; + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(branch_, node_delete_list); + } + + std::size_t node_depth() const exprtk_override exprtk_final + { + return expression_node::ndb_t::template compute_node_depth<4>(branch_); + } + + inline bool valid() const exprtk_override + { + return + branch_[0].first && branch_[0].first->valid() && + branch_[1].first && branch_[1].first->valid() && + branch_[2].first && branch_[2].first->valid() && + branch_[3].first && branch_[3].first->valid() ; + } + + protected: + + operator_type operation_; + branch_t branch_[4]; + }; + + template + class conditional_node exprtk_final : public expression_node + { + public: + + typedef expression_node* expression_ptr; + typedef std::pair branch_t; + + conditional_node(expression_ptr condition, + expression_ptr consequent, + expression_ptr alternative) + { + construct_branch_pair(condition_ , condition ); + construct_branch_pair(consequent_ , consequent ); + construct_branch_pair(alternative_, alternative); + assert(valid()); + } + + inline T value() const exprtk_override + { + if (is_true(condition_)) + return consequent_.first->value(); + else + return alternative_.first->value(); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_conditional; + } + + inline bool valid() const exprtk_override + { + return + condition_ .first && condition_ .first->valid() && + consequent_ .first && consequent_ .first->valid() && + alternative_.first && alternative_.first->valid() ; + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(condition_ , node_delete_list); + expression_node::ndb_t::collect(consequent_ , node_delete_list); + expression_node::ndb_t::collect(alternative_ , node_delete_list); + } + + std::size_t node_depth() const exprtk_override + { + return expression_node::ndb_t::compute_node_depth + (condition_, consequent_, alternative_); + } + + private: + + branch_t condition_; + branch_t consequent_; + branch_t alternative_; + }; + + template + class cons_conditional_node exprtk_final : public expression_node + { + public: + + // Consequent only conditional statement node + typedef expression_node* expression_ptr; + typedef std::pair branch_t; + + cons_conditional_node(expression_ptr condition, + expression_ptr consequent) + { + construct_branch_pair(condition_ , condition ); + construct_branch_pair(consequent_, consequent); + assert(valid()); + } + + inline T value() const exprtk_override + { + if (is_true(condition_)) + return consequent_.first->value(); + else + return std::numeric_limits::quiet_NaN(); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_conditional; + } + + inline bool valid() const exprtk_override + { + return + condition_ .first && condition_ .first->valid() && + consequent_.first && consequent_.first->valid() ; + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(condition_ , node_delete_list); + expression_node::ndb_t::collect(consequent_ , node_delete_list); + } + + std::size_t node_depth() const exprtk_override + { + return expression_node::ndb_t:: + compute_node_depth(condition_, consequent_); + } + + private: + + branch_t condition_; + branch_t consequent_; + }; + + #ifndef exprtk_disable_break_continue + template + class break_exception + { + public: + + explicit break_exception(const T& v) + : value(v) + {} + + T value; + }; + + class continue_exception {}; + + template + class break_node exprtk_final : public expression_node + { + public: + + typedef expression_node* expression_ptr; + typedef std::pair branch_t; + + explicit break_node(expression_ptr ret = expression_ptr(0)) + { + construct_branch_pair(return_, ret); + } + + inline T value() const exprtk_override + { + const T result = return_.first ? + return_.first->value() : + std::numeric_limits::quiet_NaN(); + + throw break_exception(result); + + #if !defined(_MSC_VER) && !defined(__NVCOMPILER) + return std::numeric_limits::quiet_NaN(); + #endif + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_break; + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(return_, node_delete_list); + } + + std::size_t node_depth() const exprtk_override + { + return expression_node::ndb_t::compute_node_depth(return_); + } + + private: + + branch_t return_; + }; + + template + class continue_node exprtk_final : public expression_node + { + public: + + inline T value() const exprtk_override + { + throw continue_exception(); + #if !defined(_MSC_VER) && !defined(__NVCOMPILER) + return std::numeric_limits::quiet_NaN(); + #endif + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_break; + } + }; + #endif + + struct loop_runtime_checker + { + loop_runtime_checker(loop_runtime_check_ptr loop_runtime_check, + loop_runtime_check::loop_types lp_typ = loop_runtime_check::e_invalid) + : iteration_count_(0) + , loop_runtime_check_(loop_runtime_check) + , max_loop_iterations_(loop_runtime_check_->max_loop_iterations) + , loop_type_(lp_typ) + { + assert(loop_runtime_check_); + } + + inline void reset(const _uint64_t initial_value = 0) const + { + iteration_count_ = initial_value; + } + + inline bool check() const + { + assert(loop_runtime_check_); + + if ( + (++iteration_count_ <= max_loop_iterations_) && + loop_runtime_check_->check() + ) + { + return true; + } + + loop_runtime_check::violation_context ctxt; + ctxt.loop = loop_type_; + ctxt.violation = loop_runtime_check::e_iteration_count; + + loop_runtime_check_->handle_runtime_violation(ctxt); + + return false; + } + + bool valid() const + { + return 0 != loop_runtime_check_; + } + + mutable _uint64_t iteration_count_; + mutable loop_runtime_check_ptr loop_runtime_check_; + const details::_uint64_t& max_loop_iterations_; + loop_runtime_check::loop_types loop_type_; + }; + + template + class while_loop_node : public expression_node + { + public: + + typedef expression_node* expression_ptr; + typedef std::pair branch_t; + + while_loop_node(expression_ptr condition, + expression_ptr loop_body) + { + construct_branch_pair(condition_, condition); + construct_branch_pair(loop_body_, loop_body); + assert(valid()); + } + + inline T value() const exprtk_override + { + T result = T(0); + + while (is_true(condition_)) + { + result = loop_body_.first->value(); + } + + return result; + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_while; + } + + inline bool valid() const exprtk_override + { + return + condition_.first && condition_.first->valid() && + loop_body_.first && loop_body_.first->valid() ; + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(condition_ , node_delete_list); + expression_node::ndb_t::collect(loop_body_ , node_delete_list); + } + + std::size_t node_depth() const exprtk_override + { + return expression_node::ndb_t::compute_node_depth(condition_, loop_body_); + } + + protected: + + branch_t condition_; + branch_t loop_body_; + }; + + template + class while_loop_rtc_node exprtk_final + : public while_loop_node + , public loop_runtime_checker + { + public: + + typedef while_loop_node parent_t; + typedef expression_node* expression_ptr; + + while_loop_rtc_node(expression_ptr condition, + expression_ptr loop_body, + loop_runtime_check_ptr loop_rt_chk) + : parent_t(condition, loop_body) + , loop_runtime_checker(loop_rt_chk, loop_runtime_check::e_while_loop) + { + assert(valid()); + } + + inline T value() const exprtk_override + { + + T result = T(0); + + loop_runtime_checker::reset(); + + while (is_true(parent_t::condition_) && loop_runtime_checker::check()) + { + result = parent_t::loop_body_.first->value(); + } + + return result; + } + + using parent_t::valid; + + bool valid() const exprtk_override exprtk_final + { + return parent_t::valid() && + loop_runtime_checker::valid(); + } + }; + + template + class repeat_until_loop_node : public expression_node + { + public: + + typedef expression_node* expression_ptr; + typedef std::pair branch_t; + + repeat_until_loop_node(expression_ptr condition, + expression_ptr loop_body) + { + construct_branch_pair(condition_, condition); + construct_branch_pair(loop_body_, loop_body); + assert(valid()); + } + + inline T value() const exprtk_override + { + T result = T(0); + + do + { + result = loop_body_.first->value(); + } + while (is_false(condition_.first)); + + return result; + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_repeat; + } + + inline bool valid() const exprtk_override + { + return + condition_.first && condition_.first->valid() && + loop_body_.first && loop_body_.first->valid() ; + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(condition_ , node_delete_list); + expression_node::ndb_t::collect(loop_body_ , node_delete_list); + } + + std::size_t node_depth() const exprtk_override + { + return expression_node::ndb_t::compute_node_depth(condition_, loop_body_); + } + + protected: + + branch_t condition_; + branch_t loop_body_; + }; + + template + class repeat_until_loop_rtc_node exprtk_final + : public repeat_until_loop_node + , public loop_runtime_checker + { + public: + + typedef repeat_until_loop_node parent_t; + typedef expression_node* expression_ptr; + + repeat_until_loop_rtc_node(expression_ptr condition, + expression_ptr loop_body, + loop_runtime_check_ptr loop_rt_chk) + : parent_t(condition, loop_body) + , loop_runtime_checker(loop_rt_chk, loop_runtime_check::e_repeat_until_loop) + { + assert(valid()); + } + + inline T value() const exprtk_override + { + T result = T(0); + + loop_runtime_checker::reset(1); + + do + { + result = parent_t::loop_body_.first->value(); + } + while (is_false(parent_t::condition_.first) && loop_runtime_checker::check()); + + return result; + } + + using parent_t::valid; + + inline bool valid() const exprtk_override exprtk_final + { + return parent_t::valid() && + loop_runtime_checker::valid(); + } + }; + + template + class for_loop_node : public expression_node + { + public: + + typedef expression_node* expression_ptr; + typedef std::pair branch_t; + + for_loop_node(expression_ptr initialiser, + expression_ptr condition, + expression_ptr incrementor, + expression_ptr loop_body) + { + construct_branch_pair(initialiser_, initialiser); + construct_branch_pair(condition_ , condition ); + construct_branch_pair(incrementor_, incrementor); + construct_branch_pair(loop_body_ , loop_body ); + assert(valid()); + } + + inline T value() const exprtk_override + { + T result = T(0); + + if (initialiser_.first) + initialiser_.first->value(); + + if (incrementor_.first) + { + while (is_true(condition_)) + { + result = loop_body_.first->value(); + incrementor_.first->value(); + } + } + else + { + while (is_true(condition_)) + { + result = loop_body_.first->value(); + } + } + + return result; + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_for; + } + + inline bool valid() const exprtk_override + { + return condition_.first && loop_body_.first; + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(initialiser_ , node_delete_list); + expression_node::ndb_t::collect(condition_ , node_delete_list); + expression_node::ndb_t::collect(incrementor_ , node_delete_list); + expression_node::ndb_t::collect(loop_body_ , node_delete_list); + } + + std::size_t node_depth() const exprtk_override + { + return expression_node::ndb_t::compute_node_depth + (initialiser_, condition_, incrementor_, loop_body_); + } + + protected: + + branch_t initialiser_; + branch_t condition_ ; + branch_t incrementor_; + branch_t loop_body_ ; + }; + + template + class for_loop_rtc_node exprtk_final + : public for_loop_node + , public loop_runtime_checker + { + public: + + typedef for_loop_node parent_t; + typedef expression_node* expression_ptr; + + for_loop_rtc_node(expression_ptr initialiser, + expression_ptr condition, + expression_ptr incrementor, + expression_ptr loop_body, + loop_runtime_check_ptr loop_rt_chk) + : parent_t(initialiser, condition, incrementor, loop_body) + , loop_runtime_checker(loop_rt_chk, loop_runtime_check::e_for_loop) + { + assert(valid()); + } + + inline T value() const exprtk_override + { + T result = T(0); + + loop_runtime_checker::reset(); + + if (parent_t::initialiser_.first) + parent_t::initialiser_.first->value(); + + if (parent_t::incrementor_.first) + { + while (is_true(parent_t::condition_) && loop_runtime_checker::check()) + { + result = parent_t::loop_body_.first->value(); + parent_t::incrementor_.first->value(); + } + } + else + { + while (is_true(parent_t::condition_) && loop_runtime_checker::check()) + { + result = parent_t::loop_body_.first->value(); + } + } + + return result; + } + + using parent_t::valid; + + inline bool valid() const exprtk_override exprtk_final + { + return parent_t::valid() && + loop_runtime_checker::valid(); + } + }; + + #ifndef exprtk_disable_break_continue + template + class while_loop_bc_node : public while_loop_node + { + public: + + typedef while_loop_node parent_t; + typedef expression_node* expression_ptr; + + while_loop_bc_node(expression_ptr condition, + expression_ptr loop_body) + : parent_t(condition, loop_body) + { + assert(parent_t::valid()); + } + + inline T value() const exprtk_override + { + T result = T(0); + + while (is_true(parent_t::condition_)) + { + try + { + result = parent_t::loop_body_.first->value(); + } + catch(const break_exception& e) + { + return e.value; + } + catch(const continue_exception&) + {} + } + + return result; + } + }; + + template + class while_loop_bc_rtc_node exprtk_final + : public while_loop_bc_node + , public loop_runtime_checker + { + public: + + typedef while_loop_bc_node parent_t; + typedef expression_node* expression_ptr; + + while_loop_bc_rtc_node(expression_ptr condition, + expression_ptr loop_body, + loop_runtime_check_ptr loop_rt_chk) + : parent_t(condition, loop_body) + , loop_runtime_checker(loop_rt_chk, loop_runtime_check::e_while_loop) + { + assert(valid()); + } + + inline T value() const exprtk_override + { + T result = T(0); + + loop_runtime_checker::reset(); + + while (is_true(parent_t::condition_) && loop_runtime_checker::check()) + { + try + { + result = parent_t::loop_body_.first->value(); + } + catch(const break_exception& e) + { + return e.value; + } + catch(const continue_exception&) + {} + } + + return result; + } + + using parent_t::valid; + + inline bool valid() const exprtk_override exprtk_final + { + return parent_t::valid() && + loop_runtime_checker::valid(); + } + }; + + template + class repeat_until_loop_bc_node : public repeat_until_loop_node + { + public: + + typedef repeat_until_loop_node parent_t; + typedef expression_node* expression_ptr; + + repeat_until_loop_bc_node(expression_ptr condition, + expression_ptr loop_body) + : parent_t(condition, loop_body) + { + assert(parent_t::valid()); + } + + inline T value() const exprtk_override + { + T result = T(0); + + do + { + try + { + result = parent_t::loop_body_.first->value(); + } + catch(const break_exception& e) + { + return e.value; + } + catch(const continue_exception&) + {} + } + while (is_false(parent_t::condition_.first)); + + return result; + } + }; + + template + class repeat_until_loop_bc_rtc_node exprtk_final + : public repeat_until_loop_bc_node + , public loop_runtime_checker + { + public: + + typedef repeat_until_loop_bc_node parent_t; + typedef expression_node* expression_ptr; + + repeat_until_loop_bc_rtc_node(expression_ptr condition, + expression_ptr loop_body, + loop_runtime_check_ptr loop_rt_chk) + : parent_t(condition, loop_body) + , loop_runtime_checker(loop_rt_chk, loop_runtime_check::e_repeat_until_loop) + { + assert(valid()); + } + + inline T value() const exprtk_override + { + T result = T(0); + + loop_runtime_checker::reset(); + + do + { + try + { + result = parent_t::loop_body_.first->value(); + } + catch(const break_exception& e) + { + return e.value; + } + catch(const continue_exception&) + {} + } + while (is_false(parent_t::condition_.first) && loop_runtime_checker::check()); + + return result; + } + + using parent_t::valid; + + inline bool valid() const exprtk_override exprtk_final + { + return parent_t::valid() && + loop_runtime_checker::valid(); + } + }; + + template + class for_loop_bc_node : public for_loop_node + { + public: + + typedef for_loop_node parent_t; + typedef expression_node* expression_ptr; + + for_loop_bc_node(expression_ptr initialiser, + expression_ptr condition, + expression_ptr incrementor, + expression_ptr loop_body) + : parent_t(initialiser, condition, incrementor, loop_body) + { + assert(parent_t::valid()); + } + + inline T value() const exprtk_override + { + T result = T(0); + + if (parent_t::initialiser_.first) + parent_t::initialiser_.first->value(); + + if (parent_t::incrementor_.first) + { + while (is_true(parent_t::condition_)) + { + try + { + result = parent_t::loop_body_.first->value(); + } + catch(const break_exception& e) + { + return e.value; + } + catch(const continue_exception&) + {} + + parent_t::incrementor_.first->value(); + } + } + else + { + while (is_true(parent_t::condition_)) + { + try + { + result = parent_t::loop_body_.first->value(); + } + catch(const break_exception& e) + { + return e.value; + } + catch(const continue_exception&) + {} + } + } + + return result; + } + }; + + template + class for_loop_bc_rtc_node exprtk_final + : public for_loop_bc_node + , public loop_runtime_checker + { + public: + + typedef for_loop_bc_node parent_t; + typedef expression_node* expression_ptr; + + for_loop_bc_rtc_node(expression_ptr initialiser, + expression_ptr condition, + expression_ptr incrementor, + expression_ptr loop_body, + loop_runtime_check_ptr loop_rt_chk) + : parent_t(initialiser, condition, incrementor, loop_body) + , loop_runtime_checker(loop_rt_chk, loop_runtime_check::e_for_loop) + { + assert(valid()); + } + + inline T value() const exprtk_override + { + T result = T(0); + + loop_runtime_checker::reset(); + + if (parent_t::initialiser_.first) + parent_t::initialiser_.first->value(); + + if (parent_t::incrementor_.first) + { + while (is_true(parent_t::condition_) && loop_runtime_checker::check()) + { + try + { + result = parent_t::loop_body_.first->value(); + } + catch(const break_exception& e) + { + return e.value; + } + catch(const continue_exception&) + {} + + parent_t::incrementor_.first->value(); + } + } + else + { + while (is_true(parent_t::condition_) && loop_runtime_checker::check()) + { + try + { + result = parent_t::loop_body_.first->value(); + } + catch(const break_exception& e) + { + return e.value; + } + catch(const continue_exception&) + {} + } + } + + return result; + } + + using parent_t::valid; + + inline bool valid() const exprtk_override exprtk_final + { + return parent_t::valid() && + loop_runtime_checker::valid(); + } + }; + #endif + + template + class switch_node : public expression_node + { + public: + + typedef expression_node* expression_ptr; + typedef std::pair branch_t; + + template class Sequence> + explicit switch_node(const Sequence& arg_list) + { + if (1 != (arg_list.size() & 1)) + return; + + arg_list_.resize(arg_list.size()); + + for (std::size_t i = 0; i < arg_list.size(); ++i) + { + if (arg_list[i] && arg_list[i]->valid()) + { + construct_branch_pair(arg_list_[i], arg_list[i]); + } + else + { + arg_list_.clear(); + return; + } + } + + assert(valid()); + } + + inline T value() const exprtk_override + { + const std::size_t upper_bound = (arg_list_.size() - 1); + + for (std::size_t i = 0; i < upper_bound; i += 2) + { + expression_ptr condition = arg_list_[i ].first; + expression_ptr consequent = arg_list_[i + 1].first; + + if (is_true(condition)) + { + return consequent->value(); + } + } + + return arg_list_[upper_bound].first->value(); + } + + inline typename expression_node::node_type type() const exprtk_override exprtk_final + { + return expression_node::e_switch; + } + + inline bool valid() const exprtk_override + { + return !arg_list_.empty(); + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(arg_list_, node_delete_list); + } + + std::size_t node_depth() const exprtk_override exprtk_final + { + return expression_node::ndb_t::compute_node_depth(arg_list_); + } + + protected: + + std::vector arg_list_; + }; + + template + class switch_n_node exprtk_final : public switch_node + { + public: + + typedef expression_node* expression_ptr; + + template class Sequence> + explicit switch_n_node(const Sequence& arg_list) + : switch_node(arg_list) + {} + + inline T value() const exprtk_override + { + return Switch_N::process(switch_node::arg_list_); + } + }; + + template + class multi_switch_node exprtk_final : public expression_node + { + public: + + typedef expression_node* expression_ptr; + typedef std::pair branch_t; + + template class Sequence> + explicit multi_switch_node(const Sequence& arg_list) + { + if (0 != (arg_list.size() & 1)) + return; + + arg_list_.resize(arg_list.size()); + + for (std::size_t i = 0; i < arg_list.size(); ++i) + { + if (arg_list[i] && arg_list[i]->valid()) + { + construct_branch_pair(arg_list_[i], arg_list[i]); + } + else + { + arg_list_.clear(); + return; + } + } + + assert(valid()); + } + + inline T value() const exprtk_override + { + const std::size_t upper_bound = (arg_list_.size() - 1); + + T result = T(0); + + for (std::size_t i = 0; i < upper_bound; i += 2) + { + expression_ptr condition = arg_list_[i ].first; + expression_ptr consequent = arg_list_[i + 1].first; + + if (is_true(condition)) + { + result = consequent->value(); + } + } + + return result; + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_mswitch; + } + + inline bool valid() const exprtk_override + { + return !arg_list_.empty() && (0 == (arg_list_.size() % 2)); + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(arg_list_, node_delete_list); + } + + std::size_t node_depth() const exprtk_override exprtk_final + { + return expression_node::ndb_t::compute_node_depth(arg_list_); + } + + private: + + std::vector arg_list_; + }; + + template + class ivariable + { + public: + + virtual ~ivariable() + {} + + virtual T& ref() = 0; + virtual const T& ref() const = 0; + }; + + template + class variable_node exprtk_final + : public expression_node + , public ivariable + { + public: + + static T null_value; + + explicit variable_node() + : value_(&null_value) + {} + + explicit variable_node(T& v) + : value_(&v) + {} + + inline bool operator <(const variable_node& v) const + { + return this < (&v); + } + + inline T value() const exprtk_override + { + return (*value_); + } + + inline T& ref() exprtk_override + { + return (*value_); + } + + inline const T& ref() const exprtk_override + { + return (*value_); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_variable; + } + + private: + + T* value_; + }; + + template + T variable_node::null_value = T(std::numeric_limits::quiet_NaN()); + + template + struct range_pack + { + typedef expression_node* expression_node_ptr; + typedef std::pair cached_range_t; + + range_pack() + : n0_e (std::make_pair(false,expression_node_ptr(0))) + , n1_e (std::make_pair(false,expression_node_ptr(0))) + , n0_c (std::make_pair(false,0)) + , n1_c (std::make_pair(false,0)) + , cache(std::make_pair(0,0)) + {} + + void clear() + { + n0_e = std::make_pair(false,expression_node_ptr(0)); + n1_e = std::make_pair(false,expression_node_ptr(0)); + n0_c = std::make_pair(false,0); + n1_c = std::make_pair(false,0); + cache = std::make_pair(0,0); + } + + void free() + { + if (n0_e.first && n0_e.second) + { + n0_e.first = false; + + if ( + !is_variable_node(n0_e.second) && + !is_string_node (n0_e.second) + ) + { + destroy_node(n0_e.second); + } + } + + if (n1_e.first && n1_e.second) + { + n1_e.first = false; + + if ( + !is_variable_node(n1_e.second) && + !is_string_node (n1_e.second) + ) + { + destroy_node(n1_e.second); + } + } + } + + bool const_range() const + { + return ( n0_c.first && n1_c.first) && + (!n0_e.first && !n1_e.first); + } + + bool var_range() const + { + return ( n0_e.first && n1_e.first) && + (!n0_c.first && !n1_c.first); + } + + bool operator() (std::size_t& r0, std::size_t& r1, + const std::size_t& size = std::numeric_limits::max()) const + { + if (n0_c.first) + r0 = n0_c.second; + else if (n0_e.first) + { + r0 = static_cast(details::numeric::to_int64(n0_e.second->value())); + } + else + return false; + + if (n1_c.first) + r1 = n1_c.second; + else if (n1_e.first) + { + r1 = static_cast(details::numeric::to_int64(n1_e.second->value())); + } + else + return false; + + if ( + (std::numeric_limits::max() != size) && + (std::numeric_limits::max() == r1 ) + ) + { + r1 = size; + } + + cache.first = r0; + cache.second = r1; + + #ifndef exprtk_enable_range_runtime_checks + return (r0 <= r1); + #else + return range_runtime_check(r0, r1, size); + #endif + } + + inline std::size_t const_size() const + { + return (n1_c.second - n0_c.second); + } + + inline std::size_t cache_size() const + { + return (cache.second - cache.first); + } + + std::pair n0_e; + std::pair n1_e; + std::pair n0_c; + std::pair n1_c; + mutable cached_range_t cache; + + #ifdef exprtk_enable_range_runtime_checks + bool range_runtime_check(const std::size_t r0, + const std::size_t r1, + const std::size_t size) const + { + if (r0 > size) + { + throw std::runtime_error("range error: (r0 < 0) || (r0 > size)"); + #if !defined(_MSC_VER) && !defined(__NVCOMPILER) + return false; + #endif + } + + if (r1 > size) + { + throw std::runtime_error("range error: (r1 < 0) || (r1 > size)"); + #if !defined(_MSC_VER) && !defined(__NVCOMPILER) + return false; + #endif + } + + return (r0 <= r1); + } + #endif + }; + + template + class string_base_node; + + template + struct range_data_type + { + typedef range_pack range_t; + typedef string_base_node* strbase_ptr_t; + + range_data_type() + : range(0) + , data (0) + , size (0) + , type_size(0) + , str_node (0) + {} + + range_t* range; + void* data; + std::size_t size; + std::size_t type_size; + strbase_ptr_t str_node; + }; + + template class vector_node; + + template + class vector_interface + { + public: + + typedef vector_node* vector_node_ptr; + typedef vec_data_store vds_t; + + virtual ~vector_interface() + {} + + virtual std::size_t size () const = 0; + + virtual std::size_t base_size() const = 0; + + virtual vector_node_ptr vec () const = 0; + + virtual vector_node_ptr vec () = 0; + + virtual vds_t& vds () = 0; + + virtual const vds_t& vds () const = 0; + + virtual bool side_effect () const { return false; } + }; + + template + class vector_node exprtk_final + : public expression_node + , public vector_interface + { + public: + + typedef expression_node* expression_ptr; + typedef vector_holder vector_holder_t; + typedef vector_node* vector_node_ptr; + typedef vec_data_store vds_t; + + explicit vector_node(vector_holder_t* vh) + : vector_holder_(vh) + , vds_((*vector_holder_).size(),(*vector_holder_)[0]) + { + vector_holder_->set_ref(&vds_.ref()); + } + + vector_node(const vds_t& vds, vector_holder_t* vh) + : vector_holder_(vh) + , vds_(vds) + {} + + ~vector_node() exprtk_override + { + assert(valid()); + vector_holder_->remove_ref(&vds_.ref()); + } + + inline T value() const exprtk_override + { + return vds().data()[0]; + } + + vector_node_ptr vec() const exprtk_override + { + return const_cast(this); + } + + vector_node_ptr vec() exprtk_override + { + return this; + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_vector; + } + + inline bool valid() const exprtk_override + { + return vector_holder_; + } + + std::size_t size() const exprtk_override + { + return vec_holder().size(); + } + + std::size_t base_size() const exprtk_override + { + return vec_holder().base_size(); + } + + vds_t& vds() exprtk_override + { + return vds_; + } + + const vds_t& vds() const exprtk_override + { + return vds_; + } + + inline vector_holder_t& vec_holder() + { + return (*vector_holder_); + } + + inline vector_holder_t& vec_holder() const + { + return (*vector_holder_); + } + + private: + + vector_holder_t* vector_holder_; + vds_t vds_; + }; + + template + class vector_size_node exprtk_final + : public expression_node + { + public: + + typedef expression_node* expression_ptr; + typedef vector_holder vector_holder_t; + + explicit vector_size_node(vector_holder_t* vh) + : vector_holder_(vh) + {} + + ~vector_size_node() exprtk_override + { + assert(valid()); + } + + inline T value() const exprtk_override + { + assert(vector_holder_); + return static_cast(vector_holder_->size()); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_vecsize; + } + + inline bool valid() const exprtk_override + { + return vector_holder_ && vector_holder_->size(); + } + + inline vector_holder_t* vec_holder() + { + return vector_holder_; + } + + private: + + vector_holder_t* vector_holder_; + }; + + template + class vector_elem_node exprtk_final + : public expression_node + , public ivariable + { + public: + + typedef expression_node* expression_ptr; + typedef vector_holder vector_holder_t; + typedef vector_holder_t* vector_holder_ptr; + typedef std::pair branch_t; + + vector_elem_node(expression_ptr vec_node, + expression_ptr index, + vector_holder_ptr vec_holder) + : vector_holder_(vec_holder) + , vector_base_((*vec_holder)[0]) + { + construct_branch_pair(vector_node_, vec_node); + construct_branch_pair(index_ , index ); + assert(valid()); + } + + inline T value() const exprtk_override + { + return *access_vector(); + } + + inline T& ref() exprtk_override + { + return *access_vector(); + } + + inline const T& ref() const exprtk_override + { + return *access_vector(); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_vecelem; + } + + inline bool valid() const exprtk_override + { + return + vector_holder_ && + index_.first && + vector_node_.first && + index_.first->valid() && + vector_node_.first->valid(); + } + + inline vector_holder_t& vec_holder() + { + return (*vector_holder_); + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(vector_node_, node_delete_list); + expression_node::ndb_t::collect(index_ , node_delete_list); + } + + std::size_t node_depth() const exprtk_override + { + return expression_node::ndb_t::compute_node_depth + (vector_node_, index_); + } + + private: + + inline T* access_vector() const + { + vector_node_.first->value(); + return (vector_base_ + details::numeric::to_uint64(index_.first->value())); + } + + vector_holder_ptr vector_holder_; + T* vector_base_; + branch_t vector_node_; + branch_t index_; + }; + + template + class vector_celem_node exprtk_final + : public expression_node + , public ivariable + { + public: + + typedef expression_node* expression_ptr; + typedef vector_holder vector_holder_t; + typedef vector_holder_t* vector_holder_ptr; + typedef std::pair branch_t; + + vector_celem_node(expression_ptr vec_node, + const std::size_t index, + vector_holder_ptr vec_holder) + : index_(index) + , vector_holder_(vec_holder) + , vector_base_((*vec_holder)[0]) + { + construct_branch_pair(vector_node_, vec_node); + assert(valid()); + } + + inline T value() const exprtk_override + { + return *access_vector(); + } + + inline T& ref() exprtk_override + { + return *access_vector(); + } + + inline const T& ref() const exprtk_override + { + return *access_vector(); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_veccelem; + } + + inline bool valid() const exprtk_override + { + return + vector_holder_ && + vector_node_.first && + vector_node_.first->valid(); + } + + inline vector_holder_t& vec_holder() + { + return (*vector_holder_); + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(vector_node_, node_delete_list); + } + + std::size_t node_depth() const exprtk_override + { + return expression_node::ndb_t::compute_node_depth(vector_node_); + } + + private: + + inline T* access_vector() const + { + vector_node_.first->value(); + return (vector_base_ + index_); + } + + const std::size_t index_; + vector_holder_ptr vector_holder_; + T* vector_base_; + branch_t vector_node_; + }; + + template + class vector_elem_rtc_node exprtk_final + : public expression_node + , public ivariable + { + public: + + typedef expression_node* expression_ptr; + typedef vector_holder vector_holder_t; + typedef vector_holder_t* vector_holder_ptr; + typedef std::pair branch_t; + + vector_elem_rtc_node(expression_ptr vec_node, + expression_ptr index, + vector_holder_ptr vec_holder, + vector_access_runtime_check_ptr vec_rt_chk) + : vector_holder_(vec_holder) + , vector_base_((*vec_holder)[0]) + , vec_rt_chk_(vec_rt_chk) + , max_vector_index_(vector_holder_->size() - 1) + { + construct_branch_pair(vector_node_, vec_node); + construct_branch_pair(index_ , index ); + assert(valid()); + } + + inline T value() const exprtk_override + { + return *access_vector(); + } + + inline T& ref() exprtk_override + { + return *access_vector(); + } + + inline const T& ref() const exprtk_override + { + return *access_vector(); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_vecelemrtc; + } + + inline bool valid() const exprtk_override + { + return + vector_holder_ && + index_.first && + vector_node_.first && + index_.first->valid() && + vector_node_.first->valid(); + } + + inline vector_holder_t& vec_holder() + { + return (*vector_holder_); + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(vector_node_, node_delete_list); + expression_node::ndb_t::collect(index_, node_delete_list); + } + + std::size_t node_depth() const exprtk_override + { + return expression_node::ndb_t::compute_node_depth + (vector_node_, index_); + } + + private: + + inline T* access_vector() const + { + const _uint64_t index = details::numeric::to_uint64(index_.first->value()); + vector_node_.first->value(); + + if (index <= max_vector_index_) + { + return (vector_holder_->data() + index); + } + + assert(vec_rt_chk_); + + vector_access_runtime_check::violation_context context; + context.base_ptr = reinterpret_cast(vector_base_); + context.end_ptr = reinterpret_cast(vector_base_ + vector_holder_->size()); + context.access_ptr = reinterpret_cast(vector_base_ + index); + context.type_size = sizeof(T); + + return vec_rt_chk_->handle_runtime_violation(context) ? + reinterpret_cast(context.access_ptr) : + vector_base_ ; + } + + vector_holder_ptr vector_holder_; + T* vector_base_; + branch_t vector_node_; + branch_t index_; + vector_access_runtime_check_ptr vec_rt_chk_; + const std::size_t max_vector_index_; + }; + + template + class vector_celem_rtc_node exprtk_final + : public expression_node + , public ivariable + { + public: + + typedef expression_node* expression_ptr; + typedef vector_holder vector_holder_t; + typedef vector_holder_t* vector_holder_ptr; + typedef std::pair branch_t; + + vector_celem_rtc_node(expression_ptr vec_node, + const std::size_t index, + vector_holder_ptr vec_holder, + vector_access_runtime_check_ptr vec_rt_chk) + : index_(index) + , max_vector_index_(vec_holder->size() - 1) + , vector_holder_(vec_holder) + , vector_base_((*vec_holder)[0]) + , vec_rt_chk_(vec_rt_chk) + { + construct_branch_pair(vector_node_, vec_node); + assert(valid()); + } + + inline T value() const exprtk_override + { + return *access_vector(); + } + + inline T& ref() exprtk_override + { + return *access_vector(); + } + + inline const T& ref() const exprtk_override + { + return *access_vector(); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_veccelemrtc; + } + + inline bool valid() const exprtk_override + { + return + vector_holder_ && + vector_node_.first && + vector_node_.first->valid(); + } + + inline vector_holder_t& vec_holder() + { + return (*vector_holder_); + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(vector_node_, node_delete_list); + } + + std::size_t node_depth() const exprtk_override + { + return expression_node::ndb_t::compute_node_depth(vector_node_); + } + + private: + + inline T* access_vector() const + { + vector_node_.first->value(); + + if (index_ <= max_vector_index_) + { + return (vector_holder_->data() + index_); + } + + assert(vec_rt_chk_); + + vector_access_runtime_check::violation_context context; + context.base_ptr = reinterpret_cast(vector_base_); + context.end_ptr = reinterpret_cast(vector_base_ + vector_holder_->size()); + context.access_ptr = reinterpret_cast(vector_base_ + index_); + context.type_size = sizeof(T); + + return vec_rt_chk_->handle_runtime_violation(context) ? + reinterpret_cast(context.access_ptr) : + vector_base_ ; + } + + const std::size_t index_; + const std::size_t max_vector_index_; + vector_holder_ptr vector_holder_; + T* vector_base_; + branch_t vector_node_; + vector_access_runtime_check_ptr vec_rt_chk_; + }; + + template + class rebasevector_elem_node exprtk_final + : public expression_node + , public ivariable + { + public: + + typedef expression_node* expression_ptr; + typedef vector_holder vector_holder_t; + typedef vector_holder_t* vector_holder_ptr; + typedef vec_data_store vds_t; + typedef std::pair branch_t; + + rebasevector_elem_node(expression_ptr vec_node, + expression_ptr index, + vector_holder_ptr vec_holder) + : vector_holder_(vec_holder) + { + construct_branch_pair(vector_node_, vec_node); + construct_branch_pair(index_ , index ); + assert(valid()); + } + + inline T value() const exprtk_override + { + return *access_vector(); + } + + inline T& ref() exprtk_override + { + return *access_vector(); + } + + inline const T& ref() const exprtk_override + { + return *access_vector(); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_rbvecelem; + } + + inline bool valid() const exprtk_override + { + return + vector_holder_ && + index_.first && + vector_node_.first && + index_.first->valid() && + vector_node_.first->valid(); + } + + inline vector_holder_t& vec_holder() + { + return (*vector_holder_); + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(vector_node_, node_delete_list); + expression_node::ndb_t::collect(index_, node_delete_list); + } + + std::size_t node_depth() const exprtk_override + { + return expression_node::ndb_t::compute_node_depth + (vector_node_, index_); + } + + private: + + inline T* access_vector() const + { + vector_node_.first->value(); + return (vector_holder_->data() + details::numeric::to_uint64(index_.first->value())); + } + + vector_holder_ptr vector_holder_; + branch_t vector_node_; + branch_t index_; + }; + + template + class rebasevector_celem_node exprtk_final + : public expression_node + , public ivariable + { + public: + + typedef expression_node* expression_ptr; + typedef vector_holder vector_holder_t; + typedef vector_holder_t* vector_holder_ptr; + typedef std::pair branch_t; + + rebasevector_celem_node(expression_ptr vec_node, + const std::size_t index, + vector_holder_ptr vec_holder) + : index_(index) + , vector_holder_(vec_holder) + { + construct_branch_pair(vector_node_, vec_node); + assert(valid()); + } + + inline T value() const exprtk_override + { + vector_node_.first->value(); + return ref(); + } + + inline T& ref() exprtk_override + { + return *(vector_holder_->data() + index_); + } + + inline const T& ref() const exprtk_override + { + return *(vector_holder_->data() + index_); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_rbveccelem; + } + + inline bool valid() const exprtk_override + { + return + vector_holder_ && + vector_node_.first && + vector_node_.first->valid(); + } + + inline vector_holder_t& vec_holder() + { + return (*vector_holder_); + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(vector_node_, node_delete_list); + } + + std::size_t node_depth() const exprtk_override + { + return expression_node::ndb_t::compute_node_depth(vector_node_); + } + + private: + + const std::size_t index_; + vector_holder_ptr vector_holder_; + branch_t vector_node_; + }; + + template + class rebasevector_elem_rtc_node exprtk_final + : public expression_node + , public ivariable + { + public: + + typedef expression_node* expression_ptr; + typedef vector_holder vector_holder_t; + typedef vector_holder_t* vector_holder_ptr; + typedef std::pair branch_t; + + rebasevector_elem_rtc_node(expression_ptr vec_node, + expression_ptr index, + vector_holder_ptr vec_holder, + vector_access_runtime_check_ptr vec_rt_chk) + : vector_holder_(vec_holder) + , vec_rt_chk_(vec_rt_chk) + { + construct_branch_pair(vector_node_, vec_node); + construct_branch_pair(index_ , index ); + assert(valid()); + } + + inline T value() const exprtk_override + { + return *access_vector(); + } + + inline T& ref() exprtk_override + { + return *access_vector(); + } + + inline const T& ref() const exprtk_override + { + return *access_vector(); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_rbvecelemrtc; + } + + inline bool valid() const exprtk_override + { + return + vector_holder_ && + index_.first && + vector_node_.first && + index_.first->valid() && + vector_node_.first->valid(); + } + + inline vector_holder_t& vec_holder() + { + return (*vector_holder_); + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(vector_node_, node_delete_list); + expression_node::ndb_t::collect(index_ , node_delete_list); + } + + std::size_t node_depth() const exprtk_override + { + return expression_node::ndb_t::compute_node_depth + (vector_node_, index_); + } + + private: + + inline T* access_vector() const + { + vector_node_.first->value(); + const _uint64_t index = details::numeric::to_uint64(index_.first->value()); + + if (index <= (vector_holder_->size() - 1)) + { + return (vector_holder_->data() + index); + } + + assert(vec_rt_chk_); + + vector_access_runtime_check::violation_context context; + context.base_ptr = reinterpret_cast(vector_holder_->data()); + context.end_ptr = reinterpret_cast(vector_holder_->data() + vector_holder_->size()); + context.access_ptr = reinterpret_cast(vector_holder_->data() + index); + context.type_size = sizeof(T); + + return vec_rt_chk_->handle_runtime_violation(context) ? + reinterpret_cast(context.access_ptr) : + vector_holder_->data() ; + } + + vector_holder_ptr vector_holder_; + branch_t vector_node_; + branch_t index_; + vector_access_runtime_check_ptr vec_rt_chk_; + }; + + template + class rebasevector_celem_rtc_node exprtk_final + : public expression_node + , public ivariable + { + public: + + typedef expression_node* expression_ptr; + typedef vector_holder vector_holder_t; + typedef vector_holder_t* vector_holder_ptr; + typedef std::pair branch_t; + + rebasevector_celem_rtc_node(expression_ptr vec_node, + const std::size_t index, + vector_holder_ptr vec_holder, + vector_access_runtime_check_ptr vec_rt_chk) + : index_(index) + , vector_holder_(vec_holder) + , vector_base_((*vec_holder)[0]) + , vec_rt_chk_(vec_rt_chk) + { + construct_branch_pair(vector_node_, vec_node); + assert(valid()); + } + + inline T value() const exprtk_override + { + return *access_vector(); + } + + inline T& ref() exprtk_override + { + return *access_vector(); + } + + inline const T& ref() const exprtk_override + { + return *access_vector(); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_rbveccelemrtc; + } + + inline bool valid() const exprtk_override + { + return + vector_holder_ && + vector_node_.first && + vector_node_.first->valid(); + } + + inline vector_holder_t& vec_holder() + { + return (*vector_holder_); + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(vector_node_, node_delete_list); + } + + std::size_t node_depth() const exprtk_override + { + return expression_node::ndb_t::compute_node_depth(vector_node_); + } + + private: + + inline T* access_vector() const + { + vector_node_.first->value(); + + if (index_ <= vector_holder_->size() - 1) + { + return (vector_holder_->data() + index_); + } + + assert(vec_rt_chk_); + + vector_access_runtime_check::violation_context context; + context.base_ptr = reinterpret_cast(vector_base_); + context.end_ptr = reinterpret_cast(vector_base_ + vector_holder_->size()); + context.access_ptr = reinterpret_cast(vector_base_ + index_); + context.type_size = sizeof(T); + + return vec_rt_chk_->handle_runtime_violation(context) ? + reinterpret_cast(context.access_ptr) : + vector_base_ ; + } + + const std::size_t index_; + vector_holder_ptr vector_holder_; + T* vector_base_; + branch_t vector_node_; + vector_access_runtime_check_ptr vec_rt_chk_; + }; + + template + class vector_initialisation_node exprtk_final : public expression_node + { + public: + + typedef expression_node* expression_ptr; + + vector_initialisation_node(T* vector_base, + const std::size_t& size, + const std::vector& initialiser_list, + const bool single_value_initialse) + : vector_base_(vector_base) + , initialiser_list_(initialiser_list) + , size_(size) + , single_value_initialse_(single_value_initialse) + , zero_value_initialse_(false) + , const_nonzero_literal_value_initialse_(false) + , single_initialiser_value_(T(0)) + { + if (single_value_initialse_) + { + if (initialiser_list_.empty()) + zero_value_initialse_ = true; + else if ( + (initialiser_list_.size() == 1) && + details::is_constant_node(initialiser_list_[0]) && + (T(0) == initialiser_list_[0]->value()) + ) + { + zero_value_initialse_ = true; + } + else + { + assert(initialiser_list_.size() == 1); + + if (details::is_constant_node(initialiser_list_[0])) + { + const_nonzero_literal_value_initialse_ = true; + single_initialiser_value_ = initialiser_list_[0]->value(); + assert(T(0) != single_initialiser_value_); + } + } + } + } + + inline T value() const exprtk_override + { + if (single_value_initialse_) + { + if (zero_value_initialse_) + { + details::set_zero_value(vector_base_, size_); + } + else if (const_nonzero_literal_value_initialse_) + { + for (std::size_t i = 0; i < size_; ++i) + { + *(vector_base_ + i) = single_initialiser_value_; + } + } + else + { + for (std::size_t i = 0; i < size_; ++i) + { + *(vector_base_ + i) = initialiser_list_[0]->value(); + } + } + } + else + { + const std::size_t initialiser_list_size = initialiser_list_.size(); + + for (std::size_t i = 0; i < initialiser_list_size; ++i) + { + *(vector_base_ + i) = initialiser_list_[i]->value(); + } + + if (initialiser_list_size < size_) + { + details::set_zero_value( + vector_base_ + initialiser_list_size, + (size_ - initialiser_list_size)); + } + } + + return *(vector_base_); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_vecinit; + } + + inline bool valid() const exprtk_override + { + return vector_base_; + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(initialiser_list_, node_delete_list); + } + + std::size_t node_depth() const exprtk_override + { + return expression_node::ndb_t::compute_node_depth(initialiser_list_); + } + + private: + + vector_initialisation_node(const vector_initialisation_node&) exprtk_delete; + vector_initialisation_node& operator=(const vector_initialisation_node&) exprtk_delete; + + mutable T* vector_base_; + std::vector initialiser_list_; + const std::size_t size_; + const bool single_value_initialse_; + bool zero_value_initialse_; + bool const_nonzero_literal_value_initialse_; + T single_initialiser_value_; + }; + + template + class vector_init_zero_value_node exprtk_final : public expression_node + { + public: + + typedef expression_node* expression_ptr; + + vector_init_zero_value_node(T* vector_base, + const std::size_t& size, + const std::vector& initialiser_list) + : vector_base_(vector_base) + , size_(size) + , initialiser_list_(initialiser_list) + {} + + inline T value() const exprtk_override + { + details::set_zero_value(vector_base_, size_); + return *(vector_base_); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_vecinit; + } + + inline bool valid() const exprtk_override + { + return vector_base_; + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(initialiser_list_, node_delete_list); + } + + std::size_t node_depth() const exprtk_override + { + return expression_node::ndb_t::compute_node_depth(initialiser_list_); + } + + private: + + vector_init_zero_value_node(const vector_init_zero_value_node&) exprtk_delete; + vector_init_zero_value_node& operator=(const vector_init_zero_value_node&) exprtk_delete; + + mutable T* vector_base_; + const std::size_t size_; + std::vector initialiser_list_; + }; + + template + class vector_init_single_constvalue_node exprtk_final : public expression_node + { + public: + + typedef expression_node* expression_ptr; + + vector_init_single_constvalue_node(T* vector_base, + const std::size_t& size, + const std::vector& initialiser_list) + : vector_base_(vector_base) + , size_(size) + , initialiser_list_(initialiser_list) + { + single_initialiser_value_ = initialiser_list_[0]->value(); + assert(valid()); + } + + inline T value() const exprtk_override + { + for (std::size_t i = 0; i < size_; ++i) + { + *(vector_base_ + i) = single_initialiser_value_; + } + + return *(vector_base_); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_vecinit; + } + + inline bool valid() const exprtk_override + { + return vector_base_ && + (initialiser_list_.size() == 1) && + (details::is_constant_node(initialiser_list_[0])) && + (single_initialiser_value_ != T(0)); + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(initialiser_list_, node_delete_list); + } + + std::size_t node_depth() const exprtk_override + { + return expression_node::ndb_t::compute_node_depth(initialiser_list_); + } + + private: + + vector_init_single_constvalue_node(const vector_init_single_constvalue_node&) exprtk_delete; + vector_init_single_constvalue_node& operator=(const vector_init_single_constvalue_node&) exprtk_delete; + + mutable T* vector_base_; + const std::size_t size_; + std::vector initialiser_list_; + T single_initialiser_value_; + }; + + template + class vector_init_single_value_node exprtk_final : public expression_node + { + public: + + typedef expression_node* expression_ptr; + + vector_init_single_value_node(T* vector_base, + const std::size_t& size, + const std::vector& initialiser_list) + : vector_base_(vector_base) + , size_(size) + , initialiser_list_(initialiser_list) + { + assert(valid()); + } + + inline T value() const exprtk_override + { + expression_node& node = *initialiser_list_[0]; + + for (std::size_t i = 0; i < size_; ++i) + { + *(vector_base_ + i) = node.value(); + } + + return *(vector_base_); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_vecinit; + } + + inline bool valid() const exprtk_override + { + return vector_base_ && + (initialiser_list_.size() == 1) && + !details::is_constant_node(initialiser_list_[0]); + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(initialiser_list_, node_delete_list); + } + + std::size_t node_depth() const exprtk_override + { + return expression_node::ndb_t::compute_node_depth(initialiser_list_); + } + + private: + + vector_init_single_value_node(const vector_init_single_value_node&) exprtk_delete; + vector_init_single_value_node& operator=(const vector_init_single_value_node&) exprtk_delete; + + mutable T* vector_base_; + const std::size_t size_; + std::vector initialiser_list_; + }; + + template + class vector_init_iota_constconst_node exprtk_final : public expression_node + { + public: + + typedef expression_node* expression_ptr; + + vector_init_iota_constconst_node(T* vector_base, + const std::size_t& size, + const std::vector& initialiser_list) + : vector_base_(vector_base) + , size_(size) + , initialiser_list_(initialiser_list) + { + base_value_ = initialiser_list_[0]->value(); + increment_value_ = initialiser_list_[1]->value(); + + assert(valid()); + } + + inline T value() const exprtk_override + { + T value = base_value_; + + for (std::size_t i = 0; i < size_; ++i, value += increment_value_) + { + *(vector_base_ + i) = value; + } + + return *(vector_base_); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_vecinit; + } + + inline bool valid() const exprtk_override + { + return vector_base_ && + (initialiser_list_.size() == 2) && + (details::is_constant_node(initialiser_list_[0])) && + (details::is_constant_node(initialiser_list_[1])) ; + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(initialiser_list_, node_delete_list); + } + + std::size_t node_depth() const exprtk_override + { + return expression_node::ndb_t::compute_node_depth(initialiser_list_); + } + + private: + + vector_init_iota_constconst_node(const vector_init_iota_constconst_node&) exprtk_delete; + vector_init_iota_constconst_node& operator=(const vector_init_iota_constconst_node&) exprtk_delete; + + mutable T* vector_base_; + const std::size_t size_; + std::vector initialiser_list_; + T base_value_; + T increment_value_; + }; + + template + class vector_init_iota_constnconst_node exprtk_final : public expression_node + { + public: + + typedef expression_node* expression_ptr; + + vector_init_iota_constnconst_node(T* vector_base, + const std::size_t& size, + const std::vector& initialiser_list) + : vector_base_(vector_base) + , size_(size) + , initialiser_list_(initialiser_list) + { + assert(valid()); + base_value_ = initialiser_list_[0]->value(); + } + + inline T value() const exprtk_override + { + T value = base_value_; + expression_node& increment = *initialiser_list_[1]; + + for (std::size_t i = 0; i < size_; ++i, value += increment.value()) + { + *(vector_base_ + i) = value; + } + + return *(vector_base_); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_vecinit; + } + + inline bool valid() const exprtk_override + { + return vector_base_ && + (initialiser_list_.size() == 2) && + ( details::is_constant_node(initialiser_list_[0])) && + (!details::is_constant_node(initialiser_list_[1])); + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(initialiser_list_, node_delete_list); + } + + std::size_t node_depth() const exprtk_override + { + return expression_node::ndb_t::compute_node_depth(initialiser_list_); + } + + private: + + vector_init_iota_constnconst_node(const vector_init_iota_constnconst_node&) exprtk_delete; + vector_init_iota_constnconst_node& operator=(const vector_init_iota_constnconst_node&) exprtk_delete; + + mutable T* vector_base_; + const std::size_t size_; + std::vector initialiser_list_; + T base_value_; + }; + + template + class vector_init_iota_nconstconst_node exprtk_final : public expression_node + { + public: + + typedef expression_node* expression_ptr; + + vector_init_iota_nconstconst_node(T* vector_base, + const std::size_t& size, + const std::vector& initialiser_list) + : vector_base_(vector_base) + , size_(size) + , initialiser_list_(initialiser_list) + { + assert(valid()); + } + + inline T value() const exprtk_override + { + T value = initialiser_list_[0]->value(); + const T increment = initialiser_list_[1]->value(); + + for (std::size_t i = 0; i < size_; ++i, value += increment) + { + *(vector_base_ + i) = value; + } + + return *(vector_base_); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_vecinit; + } + + inline bool valid() const exprtk_override + { + return vector_base_ && + (initialiser_list_.size() == 2) && + (!details::is_constant_node(initialiser_list_[0])) && + (details::is_constant_node(initialiser_list_[1])); + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(initialiser_list_, node_delete_list); + } + + std::size_t node_depth() const exprtk_override + { + return expression_node::ndb_t::compute_node_depth(initialiser_list_); + } + + private: + + vector_init_iota_nconstconst_node(const vector_init_iota_nconstconst_node&) exprtk_delete; + vector_init_iota_nconstconst_node& operator=(const vector_init_iota_nconstconst_node&) exprtk_delete; + + mutable T* vector_base_; + const std::size_t size_; + std::vector initialiser_list_; + }; + + template + class vector_init_iota_nconstnconst_node exprtk_final : public expression_node + { + public: + + typedef expression_node* expression_ptr; + + vector_init_iota_nconstnconst_node(T* vector_base, + const std::size_t& size, + const std::vector& initialiser_list) + : vector_base_(vector_base) + , size_(size) + , initialiser_list_(initialiser_list) + { + assert(valid()); + } + + inline T value() const exprtk_override + { + T value = initialiser_list_[0]->value(); + expression_node& increment = *initialiser_list_[1]; + + for (std::size_t i = 0; i < size_; ++i, value += increment.value()) + { + *(vector_base_ + i) = value; + } + + return *(vector_base_); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_vecinit; + } + + inline bool valid() const exprtk_override + { + return vector_base_ && + (initialiser_list_.size() == 2) && + (!details::is_constant_node(initialiser_list_[0])) && + (!details::is_constant_node(initialiser_list_[1])); + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(initialiser_list_, node_delete_list); + } + + std::size_t node_depth() const exprtk_override + { + return expression_node::ndb_t::compute_node_depth(initialiser_list_); + } + + private: + + vector_init_iota_nconstnconst_node(const vector_init_iota_nconstnconst_node&) exprtk_delete; + vector_init_iota_nconstnconst_node& operator=(const vector_init_iota_nconstnconst_node&) exprtk_delete; + + mutable T* vector_base_; + const std::size_t size_; + std::vector initialiser_list_; + }; + + template + class swap_node exprtk_final : public expression_node + { + public: + + typedef expression_node* expression_ptr; + typedef variable_node* variable_node_ptr; + + swap_node(variable_node_ptr var0, variable_node_ptr var1) + : var0_(var0) + , var1_(var1) + {} + + inline T value() const exprtk_override + { + std::swap(var0_->ref(),var1_->ref()); + return var1_->ref(); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_swap; + } + + private: + + variable_node_ptr var0_; + variable_node_ptr var1_; + }; + + template + class swap_generic_node exprtk_final : public binary_node + { + public: + + typedef expression_node* expression_ptr; + typedef ivariable* ivariable_ptr; + + swap_generic_node(expression_ptr var0, expression_ptr var1) + : binary_node(details::e_swap, var0, var1) + , var0_(dynamic_cast(var0)) + , var1_(dynamic_cast(var1)) + {} + + inline T value() const exprtk_override + { + std::swap(var0_->ref(),var1_->ref()); + return var1_->ref(); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_swap; + } + + private: + + ivariable_ptr var0_; + ivariable_ptr var1_; + }; + + template + class swap_vecvec_node exprtk_final + : public binary_node + , public vector_interface + { + public: + + typedef expression_node* expression_ptr; + typedef vector_node * vector_node_ptr; + typedef vec_data_store vds_t; + + using binary_node::branch; + + swap_vecvec_node(expression_ptr branch0, + expression_ptr branch1) + : binary_node(details::e_swap, branch0, branch1) + , vec0_node_ptr_(0) + , vec1_node_ptr_(0) + , initialised_ (false) + { + if (is_ivector_node(branch(0))) + { + vector_interface* vi = reinterpret_cast*>(0); + + if (0 != (vi = dynamic_cast*>(branch(0)))) + { + vec0_node_ptr_ = vi->vec(); + vds() = vi->vds(); + } + } + + if (is_ivector_node(branch(1))) + { + vector_interface* vi = reinterpret_cast*>(0); + + if (0 != (vi = dynamic_cast*>(branch(1)))) + { + vec1_node_ptr_ = vi->vec(); + } + } + + if (vec0_node_ptr_ && vec1_node_ptr_) + { + initialised_ = size() <= base_size(); + } + + assert(valid()); + } + + inline T value() const exprtk_override + { + binary_node::branch(0)->value(); + binary_node::branch(1)->value(); + + T* vec0 = vec0_node_ptr_->vds().data(); + T* vec1 = vec1_node_ptr_->vds().data(); + + assert(size() <= base_size()); + const std::size_t n = size(); + + for (std::size_t i = 0; i < n; ++i) + { + std::swap(vec0[i],vec1[i]); + } + + return vec1_node_ptr_->value(); + } + + vector_node_ptr vec() const exprtk_override + { + return vec0_node_ptr_; + } + + vector_node_ptr vec() exprtk_override + { + return vec0_node_ptr_; + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_vecvecswap; + } + + inline bool valid() const exprtk_override + { + return initialised_ && binary_node::valid(); + } + + std::size_t size() const exprtk_override + { + return std::min( + vec0_node_ptr_->vec_holder().size(), + vec1_node_ptr_->vec_holder().size()); + } + + std::size_t base_size() const exprtk_override + { + return std::min( + vec0_node_ptr_->vec_holder().base_size(), + vec1_node_ptr_->vec_holder().base_size()); + } + + vds_t& vds() exprtk_override + { + return vds_; + } + + const vds_t& vds() const exprtk_override + { + return vds_; + } + + private: + + vector_node* vec0_node_ptr_; + vector_node* vec1_node_ptr_; + bool initialised_; + vds_t vds_; + }; + + #ifndef exprtk_disable_string_capabilities + template + class stringvar_node exprtk_final + : public expression_node + , public string_base_node + , public range_interface + { + public: + + typedef typename range_interface::range_t range_t; + + static std::string null_value; + + explicit stringvar_node() + : value_(&null_value) + {} + + explicit stringvar_node(std::string& v) + : value_(&v) + { + rp_.n0_c = std::make_pair(true,0); + rp_.n1_c = std::make_pair(true,v.size()); + rp_.cache.first = rp_.n0_c.second; + rp_.cache.second = rp_.n1_c.second; + } + + inline bool operator <(const stringvar_node& v) const + { + return this < (&v); + } + + inline T value() const exprtk_override + { + rp_.n1_c.second = (*value_).size(); + rp_.cache.second = rp_.n1_c.second; + + return std::numeric_limits::quiet_NaN(); + } + + std::string str() const exprtk_override + { + return ref(); + } + + char_cptr base() const exprtk_override + { + return &(*value_)[0]; + } + + std::size_t size() const exprtk_override + { + return ref().size(); + } + + std::string& ref() + { + return (*value_); + } + + const std::string& ref() const + { + return (*value_); + } + + range_t& range_ref() exprtk_override + { + return rp_; + } + + const range_t& range_ref() const exprtk_override + { + return rp_; + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_stringvar; + } + + void rebase(std::string& s) + { + value_ = &s; + rp_.n0_c = std::make_pair(true,0); + rp_.n1_c = std::make_pair(true,value_->size() - 1); + rp_.cache.first = rp_.n0_c.second; + rp_.cache.second = rp_.n1_c.second; + } + + private: + + std::string* value_; + mutable range_t rp_; + }; + + template + std::string stringvar_node::null_value = std::string(""); + + template + class string_range_node exprtk_final + : public expression_node + , public string_base_node + , public range_interface + { + public: + + typedef typename range_interface::range_t range_t; + + static std::string null_value; + + explicit string_range_node(std::string& v, const range_t& rp) + : value_(&v) + , rp_(rp) + {} + + virtual ~string_range_node() + { + rp_.free(); + } + + inline bool operator <(const string_range_node& v) const + { + return this < (&v); + } + + inline T value() const exprtk_override + { + return std::numeric_limits::quiet_NaN(); + } + + inline std::string str() const exprtk_override + { + return (*value_); + } + + char_cptr base() const exprtk_override + { + return &(*value_)[0]; + } + + std::size_t size() const exprtk_override + { + return ref().size(); + } + + inline range_t range() const + { + return rp_; + } + + inline virtual std::string& ref() + { + return (*value_); + } + + inline virtual const std::string& ref() const + { + return (*value_); + } + + inline range_t& range_ref() exprtk_override + { + return rp_; + } + + inline const range_t& range_ref() const exprtk_override + { + return rp_; + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_stringvarrng; + } + + private: + + std::string* value_; + range_t rp_; + }; + + template + std::string string_range_node::null_value = std::string(""); + + template + class const_string_range_node exprtk_final + : public expression_node + , public string_base_node + , public range_interface + { + public: + + typedef typename range_interface::range_t range_t; + + explicit const_string_range_node(const std::string& v, const range_t& rp) + : value_(v) + , rp_(rp) + {} + + ~const_string_range_node() exprtk_override + { + rp_.free(); + } + + inline T value() const exprtk_override + { + return std::numeric_limits::quiet_NaN(); + } + + std::string str() const exprtk_override + { + return value_; + } + + char_cptr base() const exprtk_override + { + return value_.data(); + } + + std::size_t size() const exprtk_override + { + return value_.size(); + } + + range_t range() const + { + return rp_; + } + + range_t& range_ref() exprtk_override + { + return rp_; + } + + const range_t& range_ref() const exprtk_override + { + return rp_; + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_cstringvarrng; + } + + private: + + const_string_range_node(const const_string_range_node&) exprtk_delete; + const_string_range_node& operator=(const const_string_range_node&) exprtk_delete; + + const std::string value_; + range_t rp_; + }; + + template + class generic_string_range_node exprtk_final + : public expression_node + , public string_base_node + , public range_interface + { + public: + + typedef expression_node * expression_ptr; + typedef stringvar_node * strvar_node_ptr; + typedef string_base_node* str_base_ptr; + typedef typename range_interface::range_t range_t; + typedef range_t* range_ptr; + typedef range_interface irange_t; + typedef irange_t* irange_ptr; + typedef std::pair branch_t; + + generic_string_range_node(expression_ptr str_branch, const range_t& brange) + : initialised_(false) + , str_base_ptr_ (0) + , str_range_ptr_(0) + , base_range_(brange) + { + range_.n0_c = std::make_pair(true,0); + range_.n1_c = std::make_pair(true,0); + range_.cache.first = range_.n0_c.second; + range_.cache.second = range_.n1_c.second; + + construct_branch_pair(branch_, str_branch); + + if (is_generally_string_node(branch_.first)) + { + str_base_ptr_ = dynamic_cast(branch_.first); + + if (0 == str_base_ptr_) + return; + + str_range_ptr_ = dynamic_cast(branch_.first); + + if (0 == str_range_ptr_) + return; + } + + initialised_ = (str_base_ptr_ && str_range_ptr_); + assert(valid()); + } + + ~generic_string_range_node() exprtk_override + { + base_range_.free(); + } + + inline T value() const exprtk_override + { + branch_.first->value(); + + std::size_t str_r0 = 0; + std::size_t str_r1 = 0; + + std::size_t r0 = 0; + std::size_t r1 = 0; + + const range_t& range = str_range_ptr_->range_ref(); + + const std::size_t base_str_size = str_base_ptr_->size(); + + if ( + range (str_r0, str_r1, base_str_size ) && + base_range_(r0 , r1 , base_str_size - str_r0) + ) + { + const std::size_t size = r1 - r0; + + range_.n1_c.second = size; + range_.cache.second = range_.n1_c.second; + + value_.assign(str_base_ptr_->base() + str_r0 + r0, size); + } + + return std::numeric_limits::quiet_NaN(); + } + + std::string str() const exprtk_override + { + return value_; + } + + char_cptr base() const exprtk_override + { + return &value_[0]; + } + + std::size_t size() const exprtk_override + { + return value_.size(); + } + + range_t& range_ref() exprtk_override + { + return range_; + } + + const range_t& range_ref() const exprtk_override + { + return range_; + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_strgenrange; + } + + inline bool valid() const exprtk_override + { + return initialised_ && branch_.first; + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(branch_, node_delete_list); + } + + std::size_t node_depth() const exprtk_override + { + return expression_node::ndb_t::compute_node_depth(branch_); + } + + private: + + bool initialised_; + branch_t branch_; + str_base_ptr str_base_ptr_; + irange_ptr str_range_ptr_; + mutable range_t base_range_; + mutable range_t range_; + mutable std::string value_; + }; + + template + class string_concat_node exprtk_final + : public binary_node + , public string_base_node + , public range_interface + { + public: + + typedef typename range_interface::range_t range_t; + typedef range_interface irange_t; + typedef irange_t* irange_ptr; + typedef range_t* range_ptr; + typedef expression_node * expression_ptr; + typedef string_base_node* str_base_ptr; + + using binary_node::branch; + + string_concat_node(const operator_type& opr, + expression_ptr branch0, + expression_ptr branch1) + : binary_node(opr, branch0, branch1) + , initialised_(false) + , str0_base_ptr_ (0) + , str1_base_ptr_ (0) + , str0_range_ptr_(0) + , str1_range_ptr_(0) + { + range_.n0_c = std::make_pair(true,0); + range_.n1_c = std::make_pair(true,0); + + range_.cache.first = range_.n0_c.second; + range_.cache.second = range_.n1_c.second; + + if (is_generally_string_node(branch(0))) + { + str0_base_ptr_ = dynamic_cast(branch(0)); + + if (0 == str0_base_ptr_) + return; + + str0_range_ptr_ = dynamic_cast(branch(0)); + + if (0 == str0_range_ptr_) + return; + } + + if (is_generally_string_node(branch(1))) + { + str1_base_ptr_ = dynamic_cast(branch(1)); + + if (0 == str1_base_ptr_) + return; + + str1_range_ptr_ = dynamic_cast(branch(1)); + + if (0 == str1_range_ptr_) + return; + } + + initialised_ = str0_base_ptr_ && + str1_base_ptr_ && + str0_range_ptr_ && + str1_range_ptr_ ; + + assert(valid()); + } + + inline T value() const exprtk_override + { + branch(0)->value(); + branch(1)->value(); + + std::size_t str0_r0 = 0; + std::size_t str0_r1 = 0; + + std::size_t str1_r0 = 0; + std::size_t str1_r1 = 0; + + const range_t& range0 = str0_range_ptr_->range_ref(); + const range_t& range1 = str1_range_ptr_->range_ref(); + + if ( + range0(str0_r0, str0_r1, str0_base_ptr_->size()) && + range1(str1_r0, str1_r1, str1_base_ptr_->size()) + ) + { + const std::size_t size0 = (str0_r1 - str0_r0); + const std::size_t size1 = (str1_r1 - str1_r0); + + value_.assign(str0_base_ptr_->base() + str0_r0, size0); + value_.append(str1_base_ptr_->base() + str1_r0, size1); + + range_.n1_c.second = value_.size(); + range_.cache.second = range_.n1_c.second; + } + + return std::numeric_limits::quiet_NaN(); + } + + std::string str() const exprtk_override + { + return value_; + } + + char_cptr base() const exprtk_override + { + return &value_[0]; + } + + std::size_t size() const exprtk_override + { + return value_.size(); + } + + range_t& range_ref() exprtk_override + { + return range_; + } + + const range_t& range_ref() const exprtk_override + { + return range_; + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_strconcat; + } + + inline bool valid() const exprtk_override + { + return initialised_ && binary_node::valid(); + } + + private: + + bool initialised_; + str_base_ptr str0_base_ptr_; + str_base_ptr str1_base_ptr_; + irange_ptr str0_range_ptr_; + irange_ptr str1_range_ptr_; + mutable range_t range_; + mutable std::string value_; + }; + + template + class swap_string_node exprtk_final + : public binary_node + , public string_base_node + , public range_interface + { + public: + + typedef typename range_interface::range_t range_t; + typedef range_t* range_ptr; + typedef range_interface irange_t; + typedef irange_t* irange_ptr; + typedef expression_node * expression_ptr; + typedef stringvar_node * strvar_node_ptr; + typedef string_base_node* str_base_ptr; + + using binary_node::branch; + + swap_string_node(expression_ptr branch0, expression_ptr branch1) + : binary_node(details::e_swap, branch0, branch1) + , initialised_(false) + , str0_node_ptr_(0) + , str1_node_ptr_(0) + { + if (is_string_node(branch(0))) + { + str0_node_ptr_ = static_cast(branch(0)); + } + + if (is_string_node(branch(1))) + { + str1_node_ptr_ = static_cast(branch(1)); + } + + initialised_ = (str0_node_ptr_ && str1_node_ptr_); + assert(valid()); + } + + inline T value() const exprtk_override + { + branch(0)->value(); + branch(1)->value(); + + std::swap(str0_node_ptr_->ref(), str1_node_ptr_->ref()); + + return std::numeric_limits::quiet_NaN(); + } + + std::string str() const exprtk_override + { + return str0_node_ptr_->str(); + } + + char_cptr base() const exprtk_override + { + return str0_node_ptr_->base(); + } + + std::size_t size() const exprtk_override + { + return str0_node_ptr_->size(); + } + + range_t& range_ref() exprtk_override + { + return str0_node_ptr_->range_ref(); + } + + const range_t& range_ref() const exprtk_override + { + return str0_node_ptr_->range_ref(); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_strswap; + } + + inline bool valid() const exprtk_override + { + return initialised_ && binary_node::valid(); + } + + private: + + bool initialised_; + strvar_node_ptr str0_node_ptr_; + strvar_node_ptr str1_node_ptr_; + }; + + template + class swap_genstrings_node exprtk_final : public binary_node + { + public: + + typedef typename range_interface::range_t range_t; + typedef range_t* range_ptr; + typedef range_interface irange_t; + typedef irange_t* irange_ptr; + typedef expression_node * expression_ptr; + typedef string_base_node* str_base_ptr; + + using binary_node::branch; + + swap_genstrings_node(expression_ptr branch0, + expression_ptr branch1) + : binary_node(details::e_default, branch0, branch1) + , str0_base_ptr_ (0) + , str1_base_ptr_ (0) + , str0_range_ptr_(0) + , str1_range_ptr_(0) + , initialised_(false) + { + if (is_generally_string_node(branch(0))) + { + str0_base_ptr_ = dynamic_cast(branch(0)); + + if (0 == str0_base_ptr_) + return; + + irange_ptr range = dynamic_cast(branch(0)); + + if (0 == range) + return; + + str0_range_ptr_ = &(range->range_ref()); + } + + if (is_generally_string_node(branch(1))) + { + str1_base_ptr_ = dynamic_cast(branch(1)); + + if (0 == str1_base_ptr_) + return; + + irange_ptr range = dynamic_cast(branch(1)); + + if (0 == range) + return; + + str1_range_ptr_ = &(range->range_ref()); + } + + initialised_ = str0_base_ptr_ && + str1_base_ptr_ && + str0_range_ptr_ && + str1_range_ptr_ ; + + assert(valid()); + } + + inline T value() const exprtk_override + { + branch(0)->value(); + branch(1)->value(); + + std::size_t str0_r0 = 0; + std::size_t str0_r1 = 0; + + std::size_t str1_r0 = 0; + std::size_t str1_r1 = 0; + + const range_t& range0 = (*str0_range_ptr_); + const range_t& range1 = (*str1_range_ptr_); + + if ( + range0(str0_r0, str0_r1, str0_base_ptr_->size()) && + range1(str1_r0, str1_r1, str1_base_ptr_->size()) + ) + { + const std::size_t size0 = range0.cache_size(); + const std::size_t size1 = range1.cache_size(); + const std::size_t max_size = std::min(size0,size1); + + char_ptr s0 = const_cast(str0_base_ptr_->base() + str0_r0); + char_ptr s1 = const_cast(str1_base_ptr_->base() + str1_r0); + + loop_unroll::details lud(max_size); + char_cptr upper_bound = s0 + lud.upper_bound; + + while (s0 < upper_bound) + { + #define exprtk_loop(N) \ + std::swap(s0[N], s1[N]); \ + + exprtk_loop( 0) exprtk_loop( 1) + exprtk_loop( 2) exprtk_loop( 3) + #ifndef exprtk_disable_superscalar_unroll + exprtk_loop( 4) exprtk_loop( 5) + exprtk_loop( 6) exprtk_loop( 7) + exprtk_loop( 8) exprtk_loop( 9) + exprtk_loop(10) exprtk_loop(11) + exprtk_loop(12) exprtk_loop(13) + exprtk_loop(14) exprtk_loop(15) + #endif + + s0 += lud.batch_size; + s1 += lud.batch_size; + } + + int i = 0; + + switch (lud.remainder) + { + #define case_stmt(N) \ + case N : { std::swap(s0[i], s1[i]); ++i; } \ + exprtk_fallthrough \ + + #ifndef exprtk_disable_superscalar_unroll + case_stmt(15) case_stmt(14) + case_stmt(13) case_stmt(12) + case_stmt(11) case_stmt(10) + case_stmt( 9) case_stmt( 8) + case_stmt( 7) case_stmt( 6) + case_stmt( 5) case_stmt( 4) + #endif + case_stmt( 3) case_stmt( 2) + case_stmt( 1) + default: break; + } + + #undef exprtk_loop + #undef case_stmt + } + + return std::numeric_limits::quiet_NaN(); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_strswap; + } + + inline bool valid() const exprtk_override + { + return initialised_ && binary_node::valid(); + } + + private: + + swap_genstrings_node(const swap_genstrings_node&) exprtk_delete; + swap_genstrings_node& operator=(const swap_genstrings_node&) exprtk_delete; + + str_base_ptr str0_base_ptr_; + str_base_ptr str1_base_ptr_; + range_ptr str0_range_ptr_; + range_ptr str1_range_ptr_; + bool initialised_; + }; + + template + class stringvar_size_node exprtk_final : public expression_node + { + public: + + static const std::string null_value; + + explicit stringvar_size_node() + : value_(&null_value) + {} + + explicit stringvar_size_node(std::string& v) + : value_(&v) + {} + + inline T value() const exprtk_override + { + return T((*value_).size()); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_stringvarsize; + } + + private: + + const std::string* value_; + }; + + template + const std::string stringvar_size_node::null_value = std::string(""); + + template + class string_size_node exprtk_final : public expression_node + { + public: + + typedef expression_node * expression_ptr; + typedef string_base_node* str_base_ptr; + typedef std::pair branch_t; + + explicit string_size_node(expression_ptr branch) + : str_base_ptr_(0) + { + construct_branch_pair(branch_, branch); + + if (is_generally_string_node(branch_.first)) + { + str_base_ptr_ = dynamic_cast(branch_.first); + } + + assert(valid()); + } + + inline T value() const exprtk_override + { + branch_.first->value(); + return T(str_base_ptr_->size()); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_stringsize; + } + + inline bool valid() const exprtk_override + { + return str_base_ptr_; + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(branch_, node_delete_list); + } + + std::size_t node_depth() const exprtk_override + { + return expression_node::ndb_t::compute_node_depth(branch_); + } + + private: + + branch_t branch_; + str_base_ptr str_base_ptr_; + }; + + struct asn_assignment + { + static inline void execute(std::string& s, char_cptr data, const std::size_t size) + { s.assign(data,size); } + }; + + struct asn_addassignment + { + static inline void execute(std::string& s, char_cptr data, const std::size_t size) + { s.append(data,size); } + }; + + template + class assignment_string_node exprtk_final + : public binary_node + , public string_base_node + , public range_interface + { + public: + + typedef typename range_interface::range_t range_t; + typedef range_t* range_ptr; + typedef range_interface irange_t; + typedef irange_t* irange_ptr; + typedef expression_node * expression_ptr; + typedef stringvar_node * strvar_node_ptr; + typedef string_base_node* str_base_ptr; + + using binary_node::branch; + + assignment_string_node(const operator_type& opr, + expression_ptr branch0, + expression_ptr branch1) + : binary_node(opr, branch0, branch1) + , initialised_(false) + , str0_base_ptr_ (0) + , str1_base_ptr_ (0) + , str0_node_ptr_ (0) + , str1_range_ptr_(0) + { + if (is_string_node(branch(0))) + { + str0_node_ptr_ = static_cast(branch(0)); + str0_base_ptr_ = dynamic_cast(branch(0)); + } + + if (is_generally_string_node(branch(1))) + { + str1_base_ptr_ = dynamic_cast(branch(1)); + + if (0 == str1_base_ptr_) + return; + + irange_ptr range = dynamic_cast(branch(1)); + + if (0 == range) + return; + + str1_range_ptr_ = &(range->range_ref()); + } + + initialised_ = str0_base_ptr_ && + str1_base_ptr_ && + str0_node_ptr_ && + str1_range_ptr_ ; + + assert(valid()); + } + + inline T value() const exprtk_override + { + branch(1)->value(); + + std::size_t r0 = 0; + std::size_t r1 = 0; + + const range_t& range = (*str1_range_ptr_); + + if (range(r0, r1, str1_base_ptr_->size())) + { + AssignmentProcess::execute( + str0_node_ptr_->ref(), + str1_base_ptr_->base() + r0, (r1 - r0)); + + branch(0)->value(); + } + + return std::numeric_limits::quiet_NaN(); + } + + std::string str() const exprtk_override + { + return str0_node_ptr_->str(); + } + + char_cptr base() const exprtk_override + { + return str0_node_ptr_->base(); + } + + std::size_t size() const exprtk_override + { + return str0_node_ptr_->size(); + } + + range_t& range_ref() exprtk_override + { + return str0_node_ptr_->range_ref(); + } + + const range_t& range_ref() const exprtk_override + { + return str0_node_ptr_->range_ref(); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_strass; + } + + inline bool valid() const exprtk_override + { + return initialised_ && binary_node::valid(); + } + + private: + + bool initialised_; + str_base_ptr str0_base_ptr_; + str_base_ptr str1_base_ptr_; + strvar_node_ptr str0_node_ptr_; + range_ptr str1_range_ptr_; + }; + + template + class assignment_string_range_node exprtk_final + : public binary_node + , public string_base_node + , public range_interface + { + public: + + typedef typename range_interface::range_t range_t; + typedef range_t* range_ptr; + typedef range_interface irange_t; + typedef irange_t* irange_ptr; + typedef expression_node * expression_ptr; + typedef stringvar_node * strvar_node_ptr; + typedef string_range_node* str_rng_node_ptr; + typedef string_base_node * str_base_ptr; + + using binary_node::branch; + + assignment_string_range_node(const operator_type& opr, + expression_ptr branch0, + expression_ptr branch1) + : binary_node(opr, branch0, branch1) + , initialised_(false) + , str0_base_ptr_ (0) + , str1_base_ptr_ (0) + , str0_rng_node_ptr_(0) + , str0_range_ptr_ (0) + , str1_range_ptr_ (0) + { + if (is_string_range_node(branch(0))) + { + str0_rng_node_ptr_ = static_cast(branch(0)); + str0_base_ptr_ = dynamic_cast(branch(0)); + irange_ptr range = dynamic_cast(branch(0)); + + if (0 == range) + return; + + str0_range_ptr_ = &(range->range_ref()); + } + + if (is_generally_string_node(branch(1))) + { + str1_base_ptr_ = dynamic_cast(branch(1)); + + if (0 == str1_base_ptr_) + return; + + irange_ptr range = dynamic_cast(branch(1)); + + if (0 == range) + return; + + str1_range_ptr_ = &(range->range_ref()); + } + + initialised_ = str0_base_ptr_ && + str1_base_ptr_ && + str0_rng_node_ptr_ && + str0_range_ptr_ && + str1_range_ptr_ ; + + assert(valid()); + } + + inline T value() const exprtk_override + { + branch(0)->value(); + branch(1)->value(); + + std::size_t s0_r0 = 0; + std::size_t s0_r1 = 0; + + std::size_t s1_r0 = 0; + std::size_t s1_r1 = 0; + + const range_t& range0 = (*str0_range_ptr_); + const range_t& range1 = (*str1_range_ptr_); + + if ( + range0(s0_r0, s0_r1, str0_base_ptr_->size()) && + range1(s1_r0, s1_r1, str1_base_ptr_->size()) + ) + { + const std::size_t size = std::min((s0_r1 - s0_r0), (s1_r1 - s1_r0)); + + std::copy( + str1_base_ptr_->base() + s1_r0, + str1_base_ptr_->base() + s1_r0 + size, + const_cast(base() + s0_r0)); + } + + return std::numeric_limits::quiet_NaN(); + } + + std::string str() const exprtk_override + { + return str0_base_ptr_->str(); + } + + char_cptr base() const exprtk_override + { + return str0_base_ptr_->base(); + } + + std::size_t size() const exprtk_override + { + return str0_base_ptr_->size(); + } + + range_t& range_ref() exprtk_override + { + return str0_rng_node_ptr_->range_ref(); + } + + const range_t& range_ref() const exprtk_override + { + return str0_rng_node_ptr_->range_ref(); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_strass; + } + + inline bool valid() const exprtk_override + { + return initialised_ && binary_node::valid(); + } + + private: + + bool initialised_; + str_base_ptr str0_base_ptr_; + str_base_ptr str1_base_ptr_; + str_rng_node_ptr str0_rng_node_ptr_; + range_ptr str0_range_ptr_; + range_ptr str1_range_ptr_; + }; + + template + class conditional_string_node exprtk_final + : public trinary_node + , public string_base_node + , public range_interface + { + public: + + typedef typename range_interface::range_t range_t; + typedef range_t* range_ptr; + typedef range_interface irange_t; + typedef irange_t* irange_ptr; + typedef expression_node * expression_ptr; + typedef string_base_node* str_base_ptr; + + conditional_string_node(expression_ptr condition, + expression_ptr consequent, + expression_ptr alternative) + : trinary_node(details::e_default, consequent, alternative, condition) + , initialised_(false) + , str0_base_ptr_ (0) + , str1_base_ptr_ (0) + , str0_range_ptr_(0) + , str1_range_ptr_(0) + , condition_ (condition ) + , consequent_ (consequent ) + , alternative_(alternative) + { + range_.n0_c = std::make_pair(true,0); + range_.n1_c = std::make_pair(true,0); + + range_.cache.first = range_.n0_c.second; + range_.cache.second = range_.n1_c.second; + + if (is_generally_string_node(trinary_node::branch_[0].first)) + { + str0_base_ptr_ = dynamic_cast(trinary_node::branch_[0].first); + + if (0 == str0_base_ptr_) + return; + + str0_range_ptr_ = dynamic_cast(trinary_node::branch_[0].first); + + if (0 == str0_range_ptr_) + return; + } + + if (is_generally_string_node(trinary_node::branch_[1].first)) + { + str1_base_ptr_ = dynamic_cast(trinary_node::branch_[1].first); + + if (0 == str1_base_ptr_) + return; + + str1_range_ptr_ = dynamic_cast(trinary_node::branch_[1].first); + + if (0 == str1_range_ptr_) + return; + } + + initialised_ = str0_base_ptr_ && + str1_base_ptr_ && + str0_range_ptr_ && + str1_range_ptr_ ; + + assert(valid()); + } + + inline T value() const exprtk_override + { + std::size_t r0 = 0; + std::size_t r1 = 0; + + if (is_true(condition_)) + { + consequent_->value(); + + const range_t& range = str0_range_ptr_->range_ref(); + + if (range(r0, r1, str0_base_ptr_->size())) + { + const std::size_t size = (r1 - r0); + + value_.assign(str0_base_ptr_->base() + r0, size); + + range_.n1_c.second = value_.size(); + range_.cache.second = range_.n1_c.second; + + return T(1); + } + } + else + { + alternative_->value(); + + const range_t& range = str1_range_ptr_->range_ref(); + + if (range(r0, r1, str1_base_ptr_->size())) + { + const std::size_t size = (r1 - r0); + + value_.assign(str1_base_ptr_->base() + r0, size); + + range_.n1_c.second = value_.size(); + range_.cache.second = range_.n1_c.second; + + return T(0); + } + } + + return std::numeric_limits::quiet_NaN(); + } + + std::string str() const exprtk_override + { + return value_; + } + + char_cptr base() const exprtk_override + { + return &value_[0]; + } + + std::size_t size() const exprtk_override + { + return value_.size(); + } + + range_t& range_ref() exprtk_override + { + return range_; + } + + const range_t& range_ref() const exprtk_override + { + return range_; + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_strcondition; + } + + inline bool valid() const exprtk_override + { + return + initialised_ && + condition_ && condition_ ->valid() && + consequent_ && consequent_ ->valid() && + alternative_&& alternative_->valid() ; + } + + private: + + bool initialised_; + str_base_ptr str0_base_ptr_; + str_base_ptr str1_base_ptr_; + irange_ptr str0_range_ptr_; + irange_ptr str1_range_ptr_; + mutable range_t range_; + mutable std::string value_; + + expression_ptr condition_; + expression_ptr consequent_; + expression_ptr alternative_; + }; + + template + class cons_conditional_str_node exprtk_final + : public binary_node + , public string_base_node + , public range_interface + { + public: + + typedef typename range_interface::range_t range_t; + typedef range_t* range_ptr; + typedef range_interface irange_t; + typedef irange_t* irange_ptr; + typedef expression_node * expression_ptr; + typedef string_base_node* str_base_ptr; + + using binary_node::branch; + + cons_conditional_str_node(expression_ptr condition, + expression_ptr consequent) + : binary_node(details::e_default, consequent, condition) + , initialised_(false) + , str0_base_ptr_ (0) + , str0_range_ptr_(0) + , condition_ (condition ) + , consequent_(consequent) + { + range_.n0_c = std::make_pair(true,0); + range_.n1_c = std::make_pair(true,0); + + range_.cache.first = range_.n0_c.second; + range_.cache.second = range_.n1_c.second; + + if (is_generally_string_node(branch(0))) + { + str0_base_ptr_ = dynamic_cast(branch(0)); + + if (0 == str0_base_ptr_) + return; + + str0_range_ptr_ = dynamic_cast(branch(0)); + + if (0 == str0_range_ptr_) + return; + } + + initialised_ = str0_base_ptr_ && str0_range_ptr_ ; + assert(valid()); + } + + inline T value() const exprtk_override + { + if (is_true(condition_)) + { + consequent_->value(); + + const range_t& range = str0_range_ptr_->range_ref(); + + std::size_t r0 = 0; + std::size_t r1 = 0; + + if (range(r0, r1, str0_base_ptr_->size())) + { + const std::size_t size = (r1 - r0); + + value_.assign(str0_base_ptr_->base() + r0, size); + + range_.n1_c.second = value_.size(); + range_.cache.second = range_.n1_c.second; + + return T(1); + } + } + + return std::numeric_limits::quiet_NaN(); + } + + std::string str() const + { + return value_; + } + + char_cptr base() const + { + return &value_[0]; + } + + std::size_t size() const + { + return value_.size(); + } + + range_t& range_ref() + { + return range_; + } + + const range_t& range_ref() const + { + return range_; + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_strccondition; + } + + inline bool valid() const exprtk_override + { + return + initialised_ && + condition_ && condition_ ->valid() && + consequent_ && consequent_ ->valid() ; + } + + private: + + bool initialised_; + str_base_ptr str0_base_ptr_; + irange_ptr str0_range_ptr_; + mutable range_t range_; + mutable std::string value_; + + expression_ptr condition_; + expression_ptr consequent_; + }; + + template + class str_vararg_node exprtk_final + : public expression_node + , public string_base_node + , public range_interface + { + public: + + typedef typename range_interface::range_t range_t; + typedef range_t* range_ptr; + typedef range_interface irange_t; + typedef irange_t* irange_ptr; + typedef expression_node * expression_ptr; + typedef string_base_node* str_base_ptr; + typedef std::pair branch_t; + + template class Sequence> + explicit str_vararg_node(const Sequence& arg_list) + : initialised_(false) + , str_base_ptr_ (0) + , str_range_ptr_(0) + { + construct_branch_pair(final_node_, const_cast(arg_list.back())); + + if (0 == final_node_.first) + return; + else if (!is_generally_string_node(final_node_.first)) + return; + + str_base_ptr_ = dynamic_cast(final_node_.first); + + if (0 == str_base_ptr_) + return; + + str_range_ptr_ = dynamic_cast(final_node_.first); + + if (0 == str_range_ptr_) + return; + + if (arg_list.size() > 1) + { + const std::size_t arg_list_size = arg_list.size() - 1; + + arg_list_.resize(arg_list_size); + + for (std::size_t i = 0; i < arg_list_size; ++i) + { + if (arg_list[i] && arg_list[i]->valid()) + { + construct_branch_pair(arg_list_[i], arg_list[i]); + } + else + { + arg_list_.clear(); + return; + } + } + + initialised_ = true; + } + + initialised_ &= str_base_ptr_ && str_range_ptr_; + assert(valid()); + } + + inline T value() const exprtk_override + { + if (!arg_list_.empty()) + { + VarArgFunction::process(arg_list_); + } + + final_node_.first->value(); + + return std::numeric_limits::quiet_NaN(); + } + + std::string str() const exprtk_override + { + return str_base_ptr_->str(); + } + + char_cptr base() const exprtk_override + { + return str_base_ptr_->base(); + } + + std::size_t size() const exprtk_override + { + return str_base_ptr_->size(); + } + + range_t& range_ref() exprtk_override + { + return str_range_ptr_->range_ref(); + } + + const range_t& range_ref() const exprtk_override + { + return str_range_ptr_->range_ref(); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_stringvararg; + } + + inline bool valid() const exprtk_override + { + return + initialised_ && + final_node_.first && final_node_.first->valid(); + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(final_node_ , node_delete_list); + expression_node::ndb_t::collect(arg_list_ , node_delete_list); + } + + std::size_t node_depth() const exprtk_override + { + return std::max( + expression_node::ndb_t::compute_node_depth(final_node_), + expression_node::ndb_t::compute_node_depth(arg_list_ )); + } + + private: + + bool initialised_; + branch_t final_node_; + str_base_ptr str_base_ptr_; + irange_ptr str_range_ptr_; + std::vector arg_list_; + }; + #endif + + template + class assert_node exprtk_final : public expression_node + { + public: + + typedef expression_node* expression_ptr; + typedef std::pair branch_t; + typedef string_base_node* str_base_ptr; + typedef assert_check::assert_context assert_context_t; + + assert_node(expression_ptr assert_condition_node, + expression_ptr assert_message_node, + assert_check_ptr assert_check, + assert_context_t context) + : assert_message_str_base_(0) + , assert_check_(assert_check) + , context_(context) + { + construct_branch_pair(assert_condition_node_, assert_condition_node); + construct_branch_pair(assert_message_node_ , assert_message_node ); + + #ifndef exprtk_disable_string_capabilities + if ( + assert_message_node_.first && + details::is_generally_string_node(assert_message_node_.first) + ) + { + assert_message_str_base_ = dynamic_cast(assert_message_node_.first); + } + #endif + + assert(valid()); + } + + inline T value() const exprtk_override + { + if (details::is_true(assert_condition_node_.first->value())) + { + return T(1); + } + + #ifndef exprtk_disable_string_capabilities + if (assert_message_node_.first) + { + assert_message_node_.first->value(); + assert(assert_message_str_base_); + context_.message = assert_message_str_base_->str(); + } + #endif + + assert_check_->handle_assert(context_); + return T(0); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_assert; + } + + inline bool valid() const exprtk_override + { + return ( + assert_check_ && + assert_condition_node_.first && + assert_condition_node_.first->valid() + ) && + ( + (0 == assert_message_node_.first) || + ( + assert_message_node_.first && + assert_message_str_base_ && + assert_message_node_.first->valid() && + details::is_generally_string_node(assert_message_node_.first) + ) + ); + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(assert_condition_node_, node_delete_list); + expression_node::ndb_t::collect(assert_message_node_ , node_delete_list); + } + + std::size_t node_depth() const exprtk_override + { + return expression_node::ndb_t::compute_node_depth + (assert_condition_node_, assert_message_node_); + } + + private: + + branch_t assert_condition_node_; + branch_t assert_message_node_; + str_base_ptr assert_message_str_base_; + assert_check_ptr assert_check_; + mutable assert_context_t context_; + }; + + template + inline T axn(const T a, const T x) + { + // a*x^n + return a * exprtk::details::numeric::fast_exp::result(x); + } + + template + inline T axnb(const T a, const T x, const T b) + { + // a*x^n+b + return a * exprtk::details::numeric::fast_exp::result(x) + b; + } + + template + struct sf_base + { + typedef typename details::functor_t::Type Type; + typedef typename details::functor_t functor_t; + typedef typename functor_t::qfunc_t quaternary_functor_t; + typedef typename functor_t::tfunc_t trinary_functor_t; + typedef typename functor_t::bfunc_t binary_functor_t; + typedef typename functor_t::ufunc_t unary_functor_t; + }; + + #define define_sfop3(NN, OP0, OP1) \ + template \ + struct sf##NN##_op : public sf_base \ + { \ + typedef typename sf_base::Type const Type; \ + static inline T process(Type x, Type y, Type z) \ + { \ + return (OP0); \ + } \ + static inline std::string id() \ + { \ + return (OP1); \ + } \ + }; \ + + define_sfop3(00,(x + y) / z ,"(t+t)/t") + define_sfop3(01,(x + y) * z ,"(t+t)*t") + define_sfop3(02,(x + y) - z ,"(t+t)-t") + define_sfop3(03,(x + y) + z ,"(t+t)+t") + define_sfop3(04,(x - y) + z ,"(t-t)+t") + define_sfop3(05,(x - y) / z ,"(t-t)/t") + define_sfop3(06,(x - y) * z ,"(t-t)*t") + define_sfop3(07,(x * y) + z ,"(t*t)+t") + define_sfop3(08,(x * y) - z ,"(t*t)-t") + define_sfop3(09,(x * y) / z ,"(t*t)/t") + define_sfop3(10,(x * y) * z ,"(t*t)*t") + define_sfop3(11,(x / y) + z ,"(t/t)+t") + define_sfop3(12,(x / y) - z ,"(t/t)-t") + define_sfop3(13,(x / y) / z ,"(t/t)/t") + define_sfop3(14,(x / y) * z ,"(t/t)*t") + define_sfop3(15,x / (y + z) ,"t/(t+t)") + define_sfop3(16,x / (y - z) ,"t/(t-t)") + define_sfop3(17,x / (y * z) ,"t/(t*t)") + define_sfop3(18,x / (y / z) ,"t/(t/t)") + define_sfop3(19,x * (y + z) ,"t*(t+t)") + define_sfop3(20,x * (y - z) ,"t*(t-t)") + define_sfop3(21,x * (y * z) ,"t*(t*t)") + define_sfop3(22,x * (y / z) ,"t*(t/t)") + define_sfop3(23,x - (y + z) ,"t-(t+t)") + define_sfop3(24,x - (y - z) ,"t-(t-t)") + define_sfop3(25,x - (y / z) ,"t-(t/t)") + define_sfop3(26,x - (y * z) ,"t-(t*t)") + define_sfop3(27,x + (y * z) ,"t+(t*t)") + define_sfop3(28,x + (y / z) ,"t+(t/t)") + define_sfop3(29,x + (y + z) ,"t+(t+t)") + define_sfop3(30,x + (y - z) ,"t+(t-t)") + define_sfop3(31,(axnb(x,y,z))," ") + define_sfop3(32,(axnb(x,y,z))," ") + define_sfop3(33,(axnb(x,y,z))," ") + define_sfop3(34,(axnb(x,y,z))," ") + define_sfop3(35,(axnb(x,y,z))," ") + define_sfop3(36,(axnb(x,y,z))," ") + define_sfop3(37,(axnb(x,y,z))," ") + define_sfop3(38,(axnb(x,y,z))," ") + define_sfop3(39,x * numeric::log(y) + z,"") + define_sfop3(40,x * numeric::log(y) - z,"") + define_sfop3(41,x * numeric::log10(y) + z,"") + define_sfop3(42,x * numeric::log10(y) - z,"") + define_sfop3(43,x * numeric::sin(y) + z ,"") + define_sfop3(44,x * numeric::sin(y) - z ,"") + define_sfop3(45,x * numeric::cos(y) + z ,"") + define_sfop3(46,x * numeric::cos(y) - z ,"") + define_sfop3(47,details::is_true(x) ? y : z,"") + + #define define_sfop4(NN, OP0, OP1) \ + template \ + struct sf##NN##_op : public sf_base \ + { \ + typedef typename sf_base::Type const Type; \ + static inline T process(Type x, Type y, Type z, Type w) \ + { \ + return (OP0); \ + } \ + static inline std::string id() \ + { \ + return (OP1); \ + } \ + }; \ + + define_sfop4(48,(x + ((y + z) / w)),"t+((t+t)/t)") + define_sfop4(49,(x + ((y + z) * w)),"t+((t+t)*t)") + define_sfop4(50,(x + ((y - z) / w)),"t+((t-t)/t)") + define_sfop4(51,(x + ((y - z) * w)),"t+((t-t)*t)") + define_sfop4(52,(x + ((y * z) / w)),"t+((t*t)/t)") + define_sfop4(53,(x + ((y * z) * w)),"t+((t*t)*t)") + define_sfop4(54,(x + ((y / z) + w)),"t+((t/t)+t)") + define_sfop4(55,(x + ((y / z) / w)),"t+((t/t)/t)") + define_sfop4(56,(x + ((y / z) * w)),"t+((t/t)*t)") + define_sfop4(57,(x - ((y + z) / w)),"t-((t+t)/t)") + define_sfop4(58,(x - ((y + z) * w)),"t-((t+t)*t)") + define_sfop4(59,(x - ((y - z) / w)),"t-((t-t)/t)") + define_sfop4(60,(x - ((y - z) * w)),"t-((t-t)*t)") + define_sfop4(61,(x - ((y * z) / w)),"t-((t*t)/t)") + define_sfop4(62,(x - ((y * z) * w)),"t-((t*t)*t)") + define_sfop4(63,(x - ((y / z) / w)),"t-((t/t)/t)") + define_sfop4(64,(x - ((y / z) * w)),"t-((t/t)*t)") + define_sfop4(65,(((x + y) * z) - w),"((t+t)*t)-t") + define_sfop4(66,(((x - y) * z) - w),"((t-t)*t)-t") + define_sfop4(67,(((x * y) * z) - w),"((t*t)*t)-t") + define_sfop4(68,(((x / y) * z) - w),"((t/t)*t)-t") + define_sfop4(69,(((x + y) / z) - w),"((t+t)/t)-t") + define_sfop4(70,(((x - y) / z) - w),"((t-t)/t)-t") + define_sfop4(71,(((x * y) / z) - w),"((t*t)/t)-t") + define_sfop4(72,(((x / y) / z) - w),"((t/t)/t)-t") + define_sfop4(73,((x * y) + (z * w)),"(t*t)+(t*t)") + define_sfop4(74,((x * y) - (z * w)),"(t*t)-(t*t)") + define_sfop4(75,((x * y) + (z / w)),"(t*t)+(t/t)") + define_sfop4(76,((x * y) - (z / w)),"(t*t)-(t/t)") + define_sfop4(77,((x / y) + (z / w)),"(t/t)+(t/t)") + define_sfop4(78,((x / y) - (z / w)),"(t/t)-(t/t)") + define_sfop4(79,((x / y) - (z * w)),"(t/t)-(t*t)") + define_sfop4(80,(x / (y + (z * w))),"t/(t+(t*t))") + define_sfop4(81,(x / (y - (z * w))),"t/(t-(t*t))") + define_sfop4(82,(x * (y + (z * w))),"t*(t+(t*t))") + define_sfop4(83,(x * (y - (z * w))),"t*(t-(t*t))") + + define_sfop4(84,(axn(x,y) + axn(z,w)),"") + define_sfop4(85,(axn(x,y) + axn(z,w)),"") + define_sfop4(86,(axn(x,y) + axn(z,w)),"") + define_sfop4(87,(axn(x,y) + axn(z,w)),"") + define_sfop4(88,(axn(x,y) + axn(z,w)),"") + define_sfop4(89,(axn(x,y) + axn(z,w)),"") + define_sfop4(90,(axn(x,y) + axn(z,w)),"") + define_sfop4(91,(axn(x,y) + axn(z,w)),"") + define_sfop4(92,((details::is_true(x) && details::is_true(y)) ? z : w),"") + define_sfop4(93,((details::is_true(x) || details::is_true(y)) ? z : w),"") + define_sfop4(94,((x < y) ? z : w),"") + define_sfop4(95,((x <= y) ? z : w),"") + define_sfop4(96,((x > y) ? z : w),"") + define_sfop4(97,((x >= y) ? z : w),"") + define_sfop4(98,(details::is_true(numeric::equal(x,y)) ? z : w),"") + define_sfop4(99,(x * numeric::sin(y) + z * numeric::cos(w)),"") + + define_sfop4(ext00,((x + y) - (z * w)),"(t+t)-(t*t)") + define_sfop4(ext01,((x + y) - (z / w)),"(t+t)-(t/t)") + define_sfop4(ext02,((x + y) + (z * w)),"(t+t)+(t*t)") + define_sfop4(ext03,((x + y) + (z / w)),"(t+t)+(t/t)") + define_sfop4(ext04,((x - y) + (z * w)),"(t-t)+(t*t)") + define_sfop4(ext05,((x - y) + (z / w)),"(t-t)+(t/t)") + define_sfop4(ext06,((x - y) - (z * w)),"(t-t)-(t*t)") + define_sfop4(ext07,((x - y) - (z / w)),"(t-t)-(t/t)") + define_sfop4(ext08,((x + y) - (z - w)),"(t+t)-(t-t)") + define_sfop4(ext09,((x + y) + (z - w)),"(t+t)+(t-t)") + define_sfop4(ext10,((x + y) + (z + w)),"(t+t)+(t+t)") + define_sfop4(ext11,((x + y) * (z - w)),"(t+t)*(t-t)") + define_sfop4(ext12,((x + y) / (z - w)),"(t+t)/(t-t)") + define_sfop4(ext13,((x - y) - (z + w)),"(t-t)-(t+t)") + define_sfop4(ext14,((x - y) + (z + w)),"(t-t)+(t+t)") + define_sfop4(ext15,((x - y) * (z + w)),"(t-t)*(t+t)") + define_sfop4(ext16,((x - y) / (z + w)),"(t-t)/(t+t)") + define_sfop4(ext17,((x * y) - (z + w)),"(t*t)-(t+t)") + define_sfop4(ext18,((x / y) - (z + w)),"(t/t)-(t+t)") + define_sfop4(ext19,((x * y) + (z + w)),"(t*t)+(t+t)") + define_sfop4(ext20,((x / y) + (z + w)),"(t/t)+(t+t)") + define_sfop4(ext21,((x * y) + (z - w)),"(t*t)+(t-t)") + define_sfop4(ext22,((x / y) + (z - w)),"(t/t)+(t-t)") + define_sfop4(ext23,((x * y) - (z - w)),"(t*t)-(t-t)") + define_sfop4(ext24,((x / y) - (z - w)),"(t/t)-(t-t)") + define_sfop4(ext25,((x + y) * (z * w)),"(t+t)*(t*t)") + define_sfop4(ext26,((x + y) * (z / w)),"(t+t)*(t/t)") + define_sfop4(ext27,((x + y) / (z * w)),"(t+t)/(t*t)") + define_sfop4(ext28,((x + y) / (z / w)),"(t+t)/(t/t)") + define_sfop4(ext29,((x - y) / (z * w)),"(t-t)/(t*t)") + define_sfop4(ext30,((x - y) / (z / w)),"(t-t)/(t/t)") + define_sfop4(ext31,((x - y) * (z * w)),"(t-t)*(t*t)") + define_sfop4(ext32,((x - y) * (z / w)),"(t-t)*(t/t)") + define_sfop4(ext33,((x * y) * (z + w)),"(t*t)*(t+t)") + define_sfop4(ext34,((x / y) * (z + w)),"(t/t)*(t+t)") + define_sfop4(ext35,((x * y) / (z + w)),"(t*t)/(t+t)") + define_sfop4(ext36,((x / y) / (z + w)),"(t/t)/(t+t)") + define_sfop4(ext37,((x * y) / (z - w)),"(t*t)/(t-t)") + define_sfop4(ext38,((x / y) / (z - w)),"(t/t)/(t-t)") + define_sfop4(ext39,((x * y) * (z - w)),"(t*t)*(t-t)") + define_sfop4(ext40,((x * y) / (z * w)),"(t*t)/(t*t)") + define_sfop4(ext41,((x / y) * (z / w)),"(t/t)*(t/t)") + define_sfop4(ext42,((x / y) * (z - w)),"(t/t)*(t-t)") + define_sfop4(ext43,((x * y) * (z * w)),"(t*t)*(t*t)") + define_sfop4(ext44,(x + (y * (z / w))),"t+(t*(t/t))") + define_sfop4(ext45,(x - (y * (z / w))),"t-(t*(t/t))") + define_sfop4(ext46,(x + (y / (z * w))),"t+(t/(t*t))") + define_sfop4(ext47,(x - (y / (z * w))),"t-(t/(t*t))") + define_sfop4(ext48,(((x - y) - z) * w),"((t-t)-t)*t") + define_sfop4(ext49,(((x - y) - z) / w),"((t-t)-t)/t") + define_sfop4(ext50,(((x - y) + z) * w),"((t-t)+t)*t") + define_sfop4(ext51,(((x - y) + z) / w),"((t-t)+t)/t") + define_sfop4(ext52,((x + (y - z)) * w),"(t+(t-t))*t") + define_sfop4(ext53,((x + (y - z)) / w),"(t+(t-t))/t") + define_sfop4(ext54,((x + y) / (z + w)),"(t+t)/(t+t)") + define_sfop4(ext55,((x - y) / (z - w)),"(t-t)/(t-t)") + define_sfop4(ext56,((x + y) * (z + w)),"(t+t)*(t+t)") + define_sfop4(ext57,((x - y) * (z - w)),"(t-t)*(t-t)") + define_sfop4(ext58,((x - y) + (z - w)),"(t-t)+(t-t)") + define_sfop4(ext59,((x - y) - (z - w)),"(t-t)-(t-t)") + define_sfop4(ext60,((x / y) + (z * w)),"(t/t)+(t*t)") + define_sfop4(ext61,(((x * y) * z) / w),"((t*t)*t)/t") + + #undef define_sfop3 + #undef define_sfop4 + + template + class sf3_node exprtk_final : public trinary_node + { + public: + + typedef expression_node* expression_ptr; + + sf3_node(const operator_type& opr, + expression_ptr branch0, + expression_ptr branch1, + expression_ptr branch2) + : trinary_node(opr, branch0, branch1, branch2) + {} + + inline T value() const exprtk_override + { + const T x = trinary_node::branch_[0].first->value(); + const T y = trinary_node::branch_[1].first->value(); + const T z = trinary_node::branch_[2].first->value(); + + return SpecialFunction::process(x, y, z); + } + }; + + template + class sf4_node exprtk_final : public quaternary_node + { + public: + + typedef expression_node* expression_ptr; + + sf4_node(const operator_type& opr, + expression_ptr branch0, + expression_ptr branch1, + expression_ptr branch2, + expression_ptr branch3) + : quaternary_node(opr, branch0, branch1, branch2, branch3) + {} + + inline T value() const exprtk_override + { + const T x = quaternary_node::branch_[0].first->value(); + const T y = quaternary_node::branch_[1].first->value(); + const T z = quaternary_node::branch_[2].first->value(); + const T w = quaternary_node::branch_[3].first->value(); + + return SpecialFunction::process(x, y, z, w); + } + }; + + template + class sf3_var_node exprtk_final : public expression_node + { + public: + + typedef expression_node* expression_ptr; + + sf3_var_node(const T& v0, const T& v1, const T& v2) + : v0_(v0) + , v1_(v1) + , v2_(v2) + {} + + inline T value() const exprtk_override + { + return SpecialFunction::process(v0_, v1_, v2_); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_trinary; + } + + private: + + sf3_var_node(const sf3_var_node&) exprtk_delete; + sf3_var_node& operator=(const sf3_var_node&) exprtk_delete; + + const T& v0_; + const T& v1_; + const T& v2_; + }; + + template + class sf4_var_node exprtk_final : public expression_node + { + public: + + typedef expression_node* expression_ptr; + + sf4_var_node(const T& v0, const T& v1, const T& v2, const T& v3) + : v0_(v0) + , v1_(v1) + , v2_(v2) + , v3_(v3) + {} + + inline T value() const exprtk_override + { + return SpecialFunction::process(v0_, v1_, v2_, v3_); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_trinary; + } + + private: + + sf4_var_node(const sf4_var_node&) exprtk_delete; + sf4_var_node& operator=(const sf4_var_node&) exprtk_delete; + + const T& v0_; + const T& v1_; + const T& v2_; + const T& v3_; + }; + + template + class vararg_node exprtk_final : public expression_node + { + public: + + typedef expression_node* expression_ptr; + typedef std::pair branch_t; + + template class Sequence> + explicit vararg_node(const Sequence& arg_list) + : initialised_(false) + { + arg_list_.resize(arg_list.size()); + + for (std::size_t i = 0; i < arg_list.size(); ++i) + { + if (arg_list[i] && arg_list[i]->valid()) + { + construct_branch_pair(arg_list_[i],arg_list[i]); + } + else + { + arg_list_.clear(); + return; + } + } + + initialised_ = (arg_list_.size() == arg_list.size()); + assert(valid()); + } + + inline T value() const exprtk_override + { + return VarArgFunction::process(arg_list_); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_vararg; + } + + inline bool valid() const exprtk_override + { + return initialised_; + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(arg_list_, node_delete_list); + } + + std::size_t node_depth() const exprtk_override + { + return expression_node::ndb_t::compute_node_depth(arg_list_); + } + + std::size_t size() const + { + return arg_list_.size(); + } + + expression_ptr operator[](const std::size_t& index) const + { + return arg_list_[index].first; + } + + private: + + std::vector arg_list_; + bool initialised_; + }; + + template + class vararg_varnode exprtk_final : public expression_node + { + public: + + typedef expression_node* expression_ptr; + + template class Sequence> + explicit vararg_varnode(const Sequence& arg_list) + : initialised_(false) + { + arg_list_.resize(arg_list.size()); + + for (std::size_t i = 0; i < arg_list.size(); ++i) + { + if (arg_list[i] && arg_list[i]->valid() && is_variable_node(arg_list[i])) + { + variable_node* var_node_ptr = static_cast*>(arg_list[i]); + arg_list_[i] = (&var_node_ptr->ref()); + } + else + { + arg_list_.clear(); + return; + } + } + + initialised_ = (arg_list.size() == arg_list_.size()); + assert(valid()); + } + + inline T value() const exprtk_override + { + return VarArgFunction::process(arg_list_); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_vararg; + } + + inline bool valid() const exprtk_override + { + return initialised_; + } + + private: + + std::vector arg_list_; + bool initialised_; + }; + + template + class vectorize_node exprtk_final : public expression_node + { + public: + + typedef expression_node* expression_ptr; + typedef std::pair branch_t; + + explicit vectorize_node(const expression_ptr v) + : ivec_ptr_(0) + { + construct_branch_pair(v_, v); + + if (is_ivector_node(v_.first)) + { + ivec_ptr_ = dynamic_cast*>(v_.first); + } + } + + inline T value() const exprtk_override + { + v_.first->value(); + return VecFunction::process(ivec_ptr_); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_vecfunc; + } + + inline bool valid() const exprtk_override + { + return ivec_ptr_ && v_.first && v_.first->valid(); + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(v_, node_delete_list); + } + + std::size_t node_depth() const exprtk_override + { + return expression_node::ndb_t::compute_node_depth(v_); + } + + private: + + vector_interface* ivec_ptr_; + branch_t v_; + }; + + template + class assignment_node exprtk_final : public binary_node + { + public: + + typedef expression_node* expression_ptr; + using binary_node::branch; + + assignment_node(const operator_type& opr, + expression_ptr branch0, + expression_ptr branch1) + : binary_node(opr, branch0, branch1) + , var_node_ptr_(0) + { + if (is_variable_node(branch(0))) + { + var_node_ptr_ = static_cast*>(branch(0)); + } + } + + inline T value() const exprtk_override + { + T& result = var_node_ptr_->ref(); + result = branch(1)->value(); + + return result; + } + + inline bool valid() const exprtk_override + { + return var_node_ptr_ && binary_node::valid(); + } + + private: + + variable_node* var_node_ptr_; + }; + + template + class assignment_vec_elem_node exprtk_final : public binary_node + { + public: + + typedef expression_node* expression_ptr; + using binary_node::branch; + + assignment_vec_elem_node(const operator_type& opr, + expression_ptr branch0, + expression_ptr branch1) + : binary_node(opr, branch0, branch1) + , vec_node_ptr_(0) + { + if (is_vector_elem_node(branch(0))) + { + vec_node_ptr_ = static_cast*>(branch(0)); + } + + assert(valid()); + } + + inline T value() const exprtk_override + { + T& result = vec_node_ptr_->ref(); + result = branch(1)->value(); + + return result; + } + + inline bool valid() const exprtk_override + { + return vec_node_ptr_ && binary_node::valid(); + } + + private: + + vector_elem_node* vec_node_ptr_; + }; + + template + class assignment_vec_elem_rtc_node exprtk_final : public binary_node + { + public: + + typedef expression_node* expression_ptr; + using binary_node::branch; + + assignment_vec_elem_rtc_node(const operator_type& opr, + expression_ptr branch0, + expression_ptr branch1) + : binary_node(opr, branch0, branch1) + , vec_node_ptr_(0) + { + if (is_vector_elem_rtc_node(branch(0))) + { + vec_node_ptr_ = static_cast*>(branch(0)); + } + + assert(valid()); + } + + inline T value() const exprtk_override + { + T& result = vec_node_ptr_->ref(); + result = branch(1)->value(); + + return result; + } + + inline bool valid() const exprtk_override + { + return vec_node_ptr_ && binary_node::valid(); + } + + private: + + vector_elem_rtc_node* vec_node_ptr_; + }; + + template + class assignment_rebasevec_elem_node exprtk_final : public binary_node + { + public: + + typedef expression_node* expression_ptr; + using expression_node::branch; + + assignment_rebasevec_elem_node(const operator_type& opr, + expression_ptr branch0, + expression_ptr branch1) + : binary_node(opr, branch0, branch1) + , rbvec_node_ptr_(0) + { + if (is_rebasevector_elem_node(branch(0))) + { + rbvec_node_ptr_ = static_cast*>(branch(0)); + } + + assert(valid()); + } + + inline T value() const exprtk_override + { + T& result = rbvec_node_ptr_->ref(); + result = branch(1)->value(); + + return result; + } + + inline bool valid() const exprtk_override + { + return rbvec_node_ptr_ && binary_node::valid(); + } + + private: + + rebasevector_elem_node* rbvec_node_ptr_; + }; + + template + class assignment_rebasevec_elem_rtc_node exprtk_final : public binary_node + { + public: + + typedef expression_node* expression_ptr; + using expression_node::branch; + + assignment_rebasevec_elem_rtc_node(const operator_type& opr, + expression_ptr branch0, + expression_ptr branch1) + : binary_node(opr, branch0, branch1) + , rbvec_node_ptr_(0) + { + if (is_rebasevector_elem_rtc_node(branch(0))) + { + rbvec_node_ptr_ = static_cast*>(branch(0)); + } + + assert(valid()); + } + + inline T value() const exprtk_override + { + T& result = rbvec_node_ptr_->ref(); + result = branch(1)->value(); + + return result; + } + + inline bool valid() const exprtk_override + { + return rbvec_node_ptr_ && binary_node::valid(); + } + + private: + + rebasevector_elem_rtc_node* rbvec_node_ptr_; + }; + + template + class assignment_rebasevec_celem_node exprtk_final : public binary_node + { + public: + + typedef expression_node* expression_ptr; + using binary_node::branch; + + assignment_rebasevec_celem_node(const operator_type& opr, + expression_ptr branch0, + expression_ptr branch1) + : binary_node(opr, branch0, branch1) + , rbvec_node_ptr_(0) + { + if (is_rebasevector_celem_node(branch(0))) + { + rbvec_node_ptr_ = static_cast*>(branch(0)); + } + + assert(valid()); + } + + inline T value() const exprtk_override + { + T& result = rbvec_node_ptr_->ref(); + result = branch(1)->value(); + + return result; + } + + inline bool valid() const exprtk_override + { + return rbvec_node_ptr_ && binary_node::valid(); + } + + private: + + rebasevector_celem_node* rbvec_node_ptr_; + }; + + template + class assignment_vec_node exprtk_final + : public binary_node + , public vector_interface + { + public: + + typedef expression_node* expression_ptr; + typedef vector_node* vector_node_ptr; + typedef vec_data_store vds_t; + + using binary_node::branch; + + assignment_vec_node(const operator_type& opr, + expression_ptr branch0, + expression_ptr branch1) + : binary_node(opr, branch0, branch1) + , vec_node_ptr_(0) + { + if (is_vector_node(branch(0))) + { + vec_node_ptr_ = static_cast*>(branch(0)); + vds() = vec_node_ptr_->vds(); + } + + assert(valid()); + } + + inline T value() const exprtk_override + { + const T v = branch(1)->value(); + + T* vec = vds().data(); + + loop_unroll::details lud(size()); + const T* upper_bound = vec + lud.upper_bound; + + while (vec < upper_bound) + { + #define exprtk_loop(N) \ + vec[N] = v; \ + + exprtk_loop( 0) exprtk_loop( 1) + exprtk_loop( 2) exprtk_loop( 3) + #ifndef exprtk_disable_superscalar_unroll + exprtk_loop( 4) exprtk_loop( 5) + exprtk_loop( 6) exprtk_loop( 7) + exprtk_loop( 8) exprtk_loop( 9) + exprtk_loop(10) exprtk_loop(11) + exprtk_loop(12) exprtk_loop(13) + exprtk_loop(14) exprtk_loop(15) + #endif + + vec += lud.batch_size; + } + + switch (lud.remainder) + { + #define case_stmt(N) \ + case N : *vec++ = v; \ + exprtk_fallthrough \ + + #ifndef exprtk_disable_superscalar_unroll + case_stmt(15) case_stmt(14) + case_stmt(13) case_stmt(12) + case_stmt(11) case_stmt(10) + case_stmt( 9) case_stmt( 8) + case_stmt( 7) case_stmt( 6) + case_stmt( 5) case_stmt( 4) + #endif + case_stmt( 3) case_stmt( 2) + case 1 : *vec++ = v; + } + + #undef exprtk_loop + #undef case_stmt + + return vec_node_ptr_->value(); + } + + vector_node_ptr vec() const exprtk_override + { + return vec_node_ptr_; + } + + vector_node_ptr vec() exprtk_override + { + return vec_node_ptr_; + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_vecvalass; + } + + inline bool valid() const exprtk_override + { + return + vec_node_ptr_ && + (vds().size() <= vec_node_ptr_->vec_holder().base_size()) && + binary_node::valid(); + } + + std::size_t size() const exprtk_override + { + return vec_node_ptr_->vec_holder().size(); + } + + std::size_t base_size() const exprtk_override + { + return vec_node_ptr_->vec_holder().base_size(); + } + + vds_t& vds() exprtk_override + { + return vds_; + } + + const vds_t& vds() const exprtk_override + { + return vds_; + } + + private: + + vector_node* vec_node_ptr_; + vds_t vds_; + }; + + template + class assignment_vecvec_node exprtk_final + : public binary_node + , public vector_interface + { + public: + + typedef expression_node* expression_ptr; + typedef vector_node* vector_node_ptr; + typedef vec_data_store vds_t; + + using binary_node::branch; + + assignment_vecvec_node(const operator_type& opr, + expression_ptr branch0, + expression_ptr branch1) + : binary_node(opr, branch0, branch1) + , vec0_node_ptr_(0) + , vec1_node_ptr_(0) + , initialised_(false) + , src_is_ivec_(false) + { + if (is_vector_node(branch(0))) + { + vec0_node_ptr_ = static_cast*>(branch(0)); + vds() = vec0_node_ptr_->vds(); + } + + if (is_vector_node(branch(1))) + { + vec1_node_ptr_ = static_cast*>(branch(1)); + vds_t::match_sizes(vds(),vec1_node_ptr_->vds()); + } + else if (is_ivector_node(branch(1))) + { + vector_interface* vi = reinterpret_cast*>(0); + + if (0 != (vi = dynamic_cast*>(branch(1)))) + { + vec1_node_ptr_ = vi->vec(); + + if (!vi->side_effect()) + { + vi->vds() = vds(); + src_is_ivec_ = true; + } + else + vds_t::match_sizes(vds(),vi->vds()); + } + } + + initialised_ = + vec0_node_ptr_ && + vec1_node_ptr_ && + (size() <= base_size()) && + (vds_.size() <= base_size()) && + binary_node::valid(); + + assert(valid()); + } + + inline T value() const exprtk_override + { + branch(1)->value(); + + if (src_is_ivec_) + return vec0_node_ptr_->value(); + + T* vec0 = vec0_node_ptr_->vds().data(); + T* vec1 = vec1_node_ptr_->vds().data(); + + loop_unroll::details lud(size()); + const T* upper_bound = vec0 + lud.upper_bound; + + while (vec0 < upper_bound) + { + #define exprtk_loop(N) \ + vec0[N] = vec1[N]; \ + + exprtk_loop( 0) exprtk_loop( 1) + exprtk_loop( 2) exprtk_loop( 3) + #ifndef exprtk_disable_superscalar_unroll + exprtk_loop( 4) exprtk_loop( 5) + exprtk_loop( 6) exprtk_loop( 7) + exprtk_loop( 8) exprtk_loop( 9) + exprtk_loop(10) exprtk_loop(11) + exprtk_loop(12) exprtk_loop(13) + exprtk_loop(14) exprtk_loop(15) + #endif + + vec0 += lud.batch_size; + vec1 += lud.batch_size; + } + + switch (lud.remainder) + { + #define case_stmt(N,fall_through) \ + case N : *vec0++ = *vec1++; \ + fall_through \ + + #ifndef exprtk_disable_superscalar_unroll + case_stmt(15, exprtk_fallthrough) case_stmt(14, exprtk_fallthrough) + case_stmt(13, exprtk_fallthrough) case_stmt(12, exprtk_fallthrough) + case_stmt(11, exprtk_fallthrough) case_stmt(10, exprtk_fallthrough) + case_stmt( 9, exprtk_fallthrough) case_stmt( 8, exprtk_fallthrough) + case_stmt( 7, exprtk_fallthrough) case_stmt( 6, exprtk_fallthrough) + case_stmt( 5, exprtk_fallthrough) case_stmt( 4, exprtk_fallthrough) + #endif + case_stmt( 3, exprtk_fallthrough) case_stmt( 2, exprtk_fallthrough) + case_stmt( 1, (void)0;) + } + + #undef exprtk_loop + #undef case_stmt + + return vec0_node_ptr_->value(); + } + + vector_node_ptr vec() exprtk_override + { + return vec0_node_ptr_; + } + + vector_node_ptr vec() const exprtk_override + { + return vec0_node_ptr_; + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_vecvecass; + } + + inline bool valid() const exprtk_override + { + return initialised_; + } + + std::size_t size() const exprtk_override + { + return std::min( + vec0_node_ptr_->vec_holder().size(), + vec1_node_ptr_->vec_holder().size()); + } + + std::size_t base_size() const exprtk_override + { + return std::min( + vec0_node_ptr_->vec_holder().base_size(), + vec1_node_ptr_->vec_holder().base_size()); + } + + vds_t& vds() exprtk_override + { + return vds_; + } + + const vds_t& vds() const exprtk_override + { + return vds_; + } + + private: + + vector_node* vec0_node_ptr_; + vector_node* vec1_node_ptr_; + bool initialised_; + bool src_is_ivec_; + vds_t vds_; + }; + + template + class assignment_op_node exprtk_final : public binary_node + { + public: + + typedef expression_node* expression_ptr; + using binary_node::branch; + + assignment_op_node(const operator_type& opr, + expression_ptr branch0, + expression_ptr branch1) + : binary_node(opr, branch0, branch1) + , var_node_ptr_(0) + { + if (is_variable_node(branch(0))) + { + var_node_ptr_ = static_cast*>(branch(0)); + } + + assert(valid()); + } + + inline T value() const exprtk_override + { + T& v = var_node_ptr_->ref(); + v = Operation::process(v,branch(1)->value()); + + return v; + } + + inline bool valid() const exprtk_override + { + return var_node_ptr_ && binary_node::valid(); + } + + private: + + variable_node* var_node_ptr_; + }; + + template + class assignment_vec_elem_op_node exprtk_final : public binary_node + { + public: + + typedef expression_node* expression_ptr; + using binary_node::branch; + + assignment_vec_elem_op_node(const operator_type& opr, + expression_ptr branch0, + expression_ptr branch1) + : binary_node(opr, branch0, branch1) + , vec_node_ptr_(0) + { + if (is_vector_elem_node(branch(0))) + { + vec_node_ptr_ = static_cast*>(branch(0)); + } + + assert(valid()); + } + + inline T value() const exprtk_override + { + T& v = vec_node_ptr_->ref(); + v = Operation::process(v,branch(1)->value()); + + return v; + } + + inline bool valid() const exprtk_override + { + return vec_node_ptr_ && binary_node::valid(); + } + + private: + + vector_elem_node* vec_node_ptr_; + }; + + template + class assignment_vec_elem_op_rtc_node exprtk_final : public binary_node + { + public: + + typedef expression_node* expression_ptr; + using binary_node::branch; + + assignment_vec_elem_op_rtc_node(const operator_type& opr, + expression_ptr branch0, + expression_ptr branch1) + : binary_node(opr, branch0, branch1) + , vec_node_ptr_(0) + { + if (is_vector_elem_rtc_node(branch(0))) + { + vec_node_ptr_ = static_cast*>(branch(0)); + } + + assert(valid()); + } + + inline T value() const exprtk_override + { + T& v = vec_node_ptr_->ref(); + v = Operation::process(v,branch(1)->value()); + + return v; + } + + inline bool valid() const exprtk_override + { + return vec_node_ptr_ && binary_node::valid(); + } + + private: + + vector_elem_rtc_node* vec_node_ptr_; + }; + + template + class assignment_vec_celem_op_rtc_node exprtk_final : public binary_node + { + public: + + typedef expression_node* expression_ptr; + using binary_node::branch; + + assignment_vec_celem_op_rtc_node(const operator_type& opr, + expression_ptr branch0, + expression_ptr branch1) + : binary_node(opr, branch0, branch1) + , vec_node_ptr_(0) + { + if (is_vector_celem_rtc_node(branch(0))) + { + vec_node_ptr_ = static_cast*>(branch(0)); + } + + assert(valid()); + } + + inline T value() const exprtk_override + { + T& v = vec_node_ptr_->ref(); + v = Operation::process(v,branch(1)->value()); + + return v; + } + + inline bool valid() const exprtk_override + { + return vec_node_ptr_ && binary_node::valid(); + } + + private: + + vector_celem_rtc_node* vec_node_ptr_; + }; + + template + class assignment_rebasevec_elem_op_node exprtk_final : public binary_node + { + public: + + typedef expression_node* expression_ptr; + using binary_node::branch; + + assignment_rebasevec_elem_op_node(const operator_type& opr, + expression_ptr branch0, + expression_ptr branch1) + : binary_node(opr, branch0, branch1) + , rbvec_node_ptr_(0) + { + if (is_rebasevector_elem_node(branch(0))) + { + rbvec_node_ptr_ = static_cast*>(branch(0)); + } + + assert(valid()); + } + + inline T value() const exprtk_override + { + T& v = rbvec_node_ptr_->ref(); + v = Operation::process(v,branch(1)->value()); + + return v; + } + + inline bool valid() const exprtk_override + { + return rbvec_node_ptr_ && binary_node::valid(); + } + + private: + + rebasevector_elem_node* rbvec_node_ptr_; + }; + + template + class assignment_rebasevec_celem_op_node exprtk_final : public binary_node + { + public: + + typedef expression_node* expression_ptr; + using binary_node::branch; + + assignment_rebasevec_celem_op_node(const operator_type& opr, + expression_ptr branch0, + expression_ptr branch1) + : binary_node(opr, branch0, branch1) + , rbvec_node_ptr_(0) + { + if (is_rebasevector_celem_node(branch(0))) + { + rbvec_node_ptr_ = static_cast*>(branch(0)); + } + + assert(valid()); + } + + inline T value() const exprtk_override + { + T& v = rbvec_node_ptr_->ref(); + v = Operation::process(v,branch(1)->value()); + + return v; + } + + inline bool valid() const exprtk_override + { + return rbvec_node_ptr_ && binary_node::valid(); + } + + private: + + rebasevector_celem_node* rbvec_node_ptr_; + }; + + template + class assignment_rebasevec_elem_op_rtc_node exprtk_final : public binary_node + { + public: + + typedef expression_node* expression_ptr; + using binary_node::branch; + + assignment_rebasevec_elem_op_rtc_node(const operator_type& opr, + expression_ptr branch0, + expression_ptr branch1) + : binary_node(opr, branch0, branch1) + , rbvec_node_ptr_(0) + { + if (is_rebasevector_elem_rtc_node(branch(0))) + { + rbvec_node_ptr_ = static_cast*>(branch(0)); + } + + assert(valid()); + } + + inline T value() const exprtk_override + { + T& v = rbvec_node_ptr_->ref(); + v = Operation::process(v,branch(1)->value()); + + return v; + } + + inline bool valid() const exprtk_override + { + return rbvec_node_ptr_ && binary_node::valid(); + } + + private: + + rebasevector_elem_rtc_node* rbvec_node_ptr_; + }; + + template + class assignment_rebasevec_celem_op_rtc_node exprtk_final : public binary_node + { + public: + + typedef expression_node* expression_ptr; + using binary_node::branch; + + assignment_rebasevec_celem_op_rtc_node(const operator_type& opr, + expression_ptr branch0, + expression_ptr branch1) + : binary_node(opr, branch0, branch1) + , rbvec_node_ptr_(0) + { + if (is_rebasevector_celem_rtc_node(branch(0))) + { + rbvec_node_ptr_ = static_cast*>(branch(0)); + } + + assert(valid()); + } + + inline T value() const exprtk_override + { + T& v = rbvec_node_ptr_->ref(); + v = Operation::process(v,branch(1)->value()); + + return v; + } + + inline bool valid() const exprtk_override + { + return rbvec_node_ptr_ && binary_node::valid(); + } + + private: + + rebasevector_celem_rtc_node* rbvec_node_ptr_; + }; + + template + class assignment_vec_op_node exprtk_final + : public binary_node + , public vector_interface + { + public: + + typedef expression_node* expression_ptr; + typedef vector_node* vector_node_ptr; + typedef vec_data_store vds_t; + + using binary_node::branch; + + assignment_vec_op_node(const operator_type& opr, + expression_ptr branch0, + expression_ptr branch1) + : binary_node(opr, branch0, branch1) + , vec_node_ptr_(0) + { + if (is_vector_node(branch(0))) + { + vec_node_ptr_ = static_cast*>(branch(0)); + vds() = vec_node_ptr_->vds(); + } + + assert(valid()); + } + + inline T value() const exprtk_override + { + const T v = branch(1)->value(); + + T* vec = vds().data(); + + loop_unroll::details lud(size()); + const T* upper_bound = vec + lud.upper_bound; + + while (vec < upper_bound) + { + #define exprtk_loop(N) \ + Operation::assign(vec[N],v); \ + + exprtk_loop( 0) exprtk_loop( 1) + exprtk_loop( 2) exprtk_loop( 3) + #ifndef exprtk_disable_superscalar_unroll + exprtk_loop( 4) exprtk_loop( 5) + exprtk_loop( 6) exprtk_loop( 7) + exprtk_loop( 8) exprtk_loop( 9) + exprtk_loop(10) exprtk_loop(11) + exprtk_loop(12) exprtk_loop(13) + exprtk_loop(14) exprtk_loop(15) + #endif + + vec += lud.batch_size; + } + + switch (lud.remainder) + { + #define case_stmt(N,fall_through) \ + case N : Operation::assign(*vec++,v); \ + fall_through \ + + #ifndef exprtk_disable_superscalar_unroll + case_stmt(15, exprtk_fallthrough) case_stmt(14, exprtk_fallthrough) + case_stmt(13, exprtk_fallthrough) case_stmt(12, exprtk_fallthrough) + case_stmt(11, exprtk_fallthrough) case_stmt(10, exprtk_fallthrough) + case_stmt( 9, exprtk_fallthrough) case_stmt( 8, exprtk_fallthrough) + case_stmt( 7, exprtk_fallthrough) case_stmt( 6, exprtk_fallthrough) + case_stmt( 5, exprtk_fallthrough) case_stmt( 4, exprtk_fallthrough) + #endif + case_stmt( 3, exprtk_fallthrough) case_stmt( 2, exprtk_fallthrough) + case_stmt( 1, (void)0;) + } + + #undef exprtk_loop + #undef case_stmt + + return vec_node_ptr_->value(); + } + + vector_node_ptr vec() const exprtk_override + { + return vec_node_ptr_; + } + + vector_node_ptr vec() exprtk_override + { + return vec_node_ptr_; + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_vecopvalass; + } + + inline bool valid() const exprtk_override + { + return + vec_node_ptr_ && + (size() <= base_size()) && + binary_node::valid() ; + } + + std::size_t size() const exprtk_override + { + return vec_node_ptr_->vec_holder().size(); + } + + std::size_t base_size() const exprtk_override + { + return vec_node_ptr_->vec_holder().base_size(); + } + + vds_t& vds() exprtk_override + { + return vds_; + } + + const vds_t& vds() const exprtk_override + { + return vds_; + } + + bool side_effect() const exprtk_override + { + return true; + } + + private: + + vector_node* vec_node_ptr_; + vds_t vds_; + }; + + template + class assignment_vecvec_op_node exprtk_final + : public binary_node + , public vector_interface + { + public: + + typedef expression_node* expression_ptr; + typedef vector_node* vector_node_ptr; + typedef vec_data_store vds_t; + + using binary_node::branch; + + assignment_vecvec_op_node(const operator_type& opr, + expression_ptr branch0, + expression_ptr branch1) + : binary_node(opr, branch0, branch1) + , vec0_node_ptr_(0) + , vec1_node_ptr_(0) + , initialised_(false) + { + if (is_vector_node(branch(0))) + { + vec0_node_ptr_ = static_cast*>(branch(0)); + vds() = vec0_node_ptr_->vds(); + } + + if (is_vector_node(branch(1))) + { + vec1_node_ptr_ = static_cast*>(branch(1)); + vec1_node_ptr_->vds() = vds(); + } + else if (is_ivector_node(branch(1))) + { + vector_interface* vi = reinterpret_cast*>(0); + + if (0 != (vi = dynamic_cast*>(branch(1)))) + { + vec1_node_ptr_ = vi->vec(); + vec1_node_ptr_->vds() = vi->vds(); + } + else + vds_t::match_sizes(vds(),vec1_node_ptr_->vds()); + } + + initialised_ = + vec0_node_ptr_ && + vec1_node_ptr_ && + (size() <= base_size()) && + binary_node::valid(); + + assert(valid()); + } + + inline T value() const exprtk_override + { + branch(0)->value(); + branch(1)->value(); + + T* vec0 = vec0_node_ptr_->vds().data(); + const T* vec1 = vec1_node_ptr_->vds().data(); + + loop_unroll::details lud(size()); + const T* upper_bound = vec0 + lud.upper_bound; + + while (vec0 < upper_bound) + { + #define exprtk_loop(N) \ + vec0[N] = Operation::process(vec0[N], vec1[N]); \ + + exprtk_loop( 0) exprtk_loop( 1) + exprtk_loop( 2) exprtk_loop( 3) + #ifndef exprtk_disable_superscalar_unroll + exprtk_loop( 4) exprtk_loop( 5) + exprtk_loop( 6) exprtk_loop( 7) + exprtk_loop( 8) exprtk_loop( 9) + exprtk_loop(10) exprtk_loop(11) + exprtk_loop(12) exprtk_loop(13) + exprtk_loop(14) exprtk_loop(15) + #endif + + vec0 += lud.batch_size; + vec1 += lud.batch_size; + } + + int i = 0; + + switch (lud.remainder) + { + #define case_stmt(N,fall_through) \ + case N : { vec0[i] = Operation::process(vec0[i], vec1[i]); ++i; } \ + fall_through \ + + #ifndef exprtk_disable_superscalar_unroll + case_stmt(15, exprtk_fallthrough) case_stmt(14, exprtk_fallthrough) + case_stmt(13, exprtk_fallthrough) case_stmt(12, exprtk_fallthrough) + case_stmt(11, exprtk_fallthrough) case_stmt(10, exprtk_fallthrough) + case_stmt( 9, exprtk_fallthrough) case_stmt( 8, exprtk_fallthrough) + case_stmt( 7, exprtk_fallthrough) case_stmt( 6, exprtk_fallthrough) + case_stmt( 5, exprtk_fallthrough) case_stmt( 4, exprtk_fallthrough) + #endif + case_stmt( 3, exprtk_fallthrough) case_stmt( 2, exprtk_fallthrough) + case_stmt( 1, (void)0;) + } + + #undef exprtk_loop + #undef case_stmt + + return vec0_node_ptr_->value(); + } + + vector_node_ptr vec() const exprtk_override + { + return vec0_node_ptr_; + } + + vector_node_ptr vec() exprtk_override + { + return vec0_node_ptr_; + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_vecopvecass; + } + + inline bool valid() const exprtk_override + { + return initialised_; + } + + std::size_t size() const exprtk_override + { + return std::min( + vec0_node_ptr_->vec_holder().size(), + vec1_node_ptr_->vec_holder().size()); + } + + std::size_t base_size() const exprtk_override + { + return std::min( + vec0_node_ptr_->vec_holder().base_size(), + vec1_node_ptr_->vec_holder().base_size()); + } + + vds_t& vds() exprtk_override + { + return vds_; + } + + const vds_t& vds() const exprtk_override + { + return vds_; + } + + bool side_effect() const exprtk_override + { + return true; + } + + private: + + vector_node* vec0_node_ptr_; + vector_node* vec1_node_ptr_; + bool initialised_; + vds_t vds_; + }; + + template + struct memory_context_t + { + typedef vector_node* vector_node_ptr; + typedef vector_holder vector_holder_t; + typedef vector_holder_t* vector_holder_ptr; + + memory_context_t() + : temp_(0) + , temp_vec_node_(0) + {} + + void clear() + { + delete temp_vec_node_; + delete temp_; + } + + vector_holder_ptr temp_; + vector_node_ptr temp_vec_node_; + }; + + template + inline memory_context_t make_memory_context(vector_holder& vec_holder, + vec_data_store& vds) + { + memory_context_t result_ctxt; + + result_ctxt.temp_ = (vec_holder.rebaseable()) ? + new vector_holder(vec_holder,vds) : + new vector_holder(vds) ; + + result_ctxt.temp_vec_node_ = new vector_node(vds,result_ctxt.temp_); + + return result_ctxt; + } + + template + inline memory_context_t make_memory_context(vector_holder& vec_holder0, + vector_holder& vec_holder1, + vec_data_store& vds) + { + memory_context_t result_ctxt; + + if (!vec_holder0.rebaseable() && !vec_holder1.rebaseable()) + result_ctxt.temp_ = new vector_holder(vds); + else if (vec_holder0.rebaseable() && !vec_holder1.rebaseable()) + result_ctxt.temp_ = new vector_holder(vec_holder0,vds); + else if (!vec_holder0.rebaseable() && vec_holder1.rebaseable()) + result_ctxt.temp_ = new vector_holder(vec_holder1,vds); + else + { + result_ctxt.temp_ = (vec_holder0.base_size() >= vec_holder1.base_size()) ? + new vector_holder(vec_holder0, vds) : + new vector_holder(vec_holder1, vds) ; + } + + result_ctxt.temp_vec_node_ = new vector_node(vds,result_ctxt.temp_); + + return result_ctxt; + } + + template + class vec_binop_vecvec_node exprtk_final + : public binary_node + , public vector_interface + { + public: + + typedef expression_node* expression_ptr; + typedef vector_node* vector_node_ptr; + typedef vector_holder vector_holder_t; + typedef vector_holder_t* vector_holder_ptr; + typedef vec_data_store vds_t; + typedef memory_context_t memory_context; + + using binary_node::branch; + + vec_binop_vecvec_node(const operator_type& opr, + expression_ptr branch0, + expression_ptr branch1) + : binary_node(opr, branch0, branch1) + , vec0_node_ptr_(0) + , vec1_node_ptr_(0) + , initialised_(false) + { + bool v0_is_ivec = false; + bool v1_is_ivec = false; + + if (is_vector_node(branch(0))) + { + vec0_node_ptr_ = static_cast(branch(0)); + } + else if (is_ivector_node(branch(0))) + { + vector_interface* vi = reinterpret_cast*>(0); + + if (0 != (vi = dynamic_cast*>(branch(0)))) + { + vec0_node_ptr_ = vi->vec(); + v0_is_ivec = true; + } + } + + if (is_vector_node(branch(1))) + { + vec1_node_ptr_ = static_cast(branch(1)); + } + else if (is_ivector_node(branch(1))) + { + vector_interface* vi = reinterpret_cast*>(0); + + if (0 != (vi = dynamic_cast*>(branch(1)))) + { + vec1_node_ptr_ = vi->vec(); + v1_is_ivec = true; + } + } + + if (vec0_node_ptr_ && vec1_node_ptr_) + { + vector_holder& vec0 = vec0_node_ptr_->vec_holder(); + vector_holder& vec1 = vec1_node_ptr_->vec_holder(); + + if (v0_is_ivec && (vec0.base_size() <= vec1.base_size())) + { + vds_ = vds_t(vec0_node_ptr_->vds()); + } + else if (v1_is_ivec && (vec1.base_size() <= vec0.base_size())) + { + vds_ = vds_t(vec1_node_ptr_->vds()); + } + else + { + vds_ = vds_t(std::min(vec0.base_size(),vec1.base_size())); + } + + memory_context_ = make_memory_context(vec0, vec1, vds()); + + initialised_ = + (size() <= base_size()) && + binary_node::valid(); + } + + assert(valid()); + } + + ~vec_binop_vecvec_node() + { + memory_context_.clear(); + } + + inline T value() const exprtk_override + { + branch(0)->value(); + branch(1)->value(); + + const T* vec0 = vec0_node_ptr_->vds().data(); + const T* vec1 = vec1_node_ptr_->vds().data(); + T* vec2 = vds().data(); + + loop_unroll::details lud(size()); + const T* upper_bound = vec2 + lud.upper_bound; + + while (vec2 < upper_bound) + { + #define exprtk_loop(N) \ + vec2[N] = Operation::process(vec0[N], vec1[N]); \ + + exprtk_loop( 0) exprtk_loop( 1) + exprtk_loop( 2) exprtk_loop( 3) + #ifndef exprtk_disable_superscalar_unroll + exprtk_loop( 4) exprtk_loop( 5) + exprtk_loop( 6) exprtk_loop( 7) + exprtk_loop( 8) exprtk_loop( 9) + exprtk_loop(10) exprtk_loop(11) + exprtk_loop(12) exprtk_loop(13) + exprtk_loop(14) exprtk_loop(15) + #endif + + vec0 += lud.batch_size; + vec1 += lud.batch_size; + vec2 += lud.batch_size; + } + + int i = 0; + + switch (lud.remainder) + { + #define case_stmt(N) \ + case N : { vec2[i] = Operation::process(vec0[i], vec1[i]); ++i; } \ + exprtk_fallthrough \ + + #ifndef exprtk_disable_superscalar_unroll + case_stmt(15) case_stmt(14) + case_stmt(13) case_stmt(12) + case_stmt(11) case_stmt(10) + case_stmt( 9) case_stmt( 8) + case_stmt( 7) case_stmt( 6) + case_stmt( 5) case_stmt( 4) + #endif + case_stmt( 3) case_stmt( 2) + case_stmt( 1) + default: break; + } + + #undef exprtk_loop + #undef case_stmt + + return (vds().data())[0]; + } + + vector_node_ptr vec() const exprtk_override + { + return memory_context_.temp_vec_node_; + } + + vector_node_ptr vec() exprtk_override + { + return memory_context_.temp_vec_node_; + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_vecvecarith; + } + + inline bool valid() const exprtk_override + { + return initialised_; + } + + std::size_t size() const exprtk_override + { + return std::min( + vec0_node_ptr_->vec_holder().size(), + vec1_node_ptr_->vec_holder().size()); + } + + std::size_t base_size() const exprtk_override + { + return std::min( + vec0_node_ptr_->vec_holder().base_size(), + vec1_node_ptr_->vec_holder().base_size()); + } + + vds_t& vds() exprtk_override + { + return vds_; + } + + const vds_t& vds() const exprtk_override + { + return vds_; + } + + private: + + vector_node_ptr vec0_node_ptr_; + vector_node_ptr vec1_node_ptr_; + bool initialised_; + vds_t vds_; + memory_context memory_context_; + }; + + template + class vec_binop_vecval_node exprtk_final + : public binary_node + , public vector_interface + { + public: + + typedef expression_node* expression_ptr; + typedef vector_node* vector_node_ptr; + typedef vector_holder vector_holder_t; + typedef vector_holder_t* vector_holder_ptr; + typedef vec_data_store vds_t; + typedef memory_context_t memory_context; + + using binary_node::branch; + + vec_binop_vecval_node(const operator_type& opr, + expression_ptr branch0, + expression_ptr branch1) + : binary_node(opr, branch0, branch1) + , vec0_node_ptr_(0) + { + bool v0_is_ivec = false; + + if (is_vector_node(branch(0))) + { + vec0_node_ptr_ = static_cast(branch(0)); + } + else if (is_ivector_node(branch(0))) + { + vector_interface* vi = reinterpret_cast*>(0); + + if (0 != (vi = dynamic_cast*>(branch(0)))) + { + vec0_node_ptr_ = vi->vec(); + v0_is_ivec = true; + } + } + + if (vec0_node_ptr_) + { + if (v0_is_ivec) + vds() = vec0_node_ptr_->vds(); + else + vds() = vds_t(vec0_node_ptr_->base_size()); + + memory_context_ = make_memory_context(vec0_node_ptr_->vec_holder(), vds()); + } + + assert(valid()); + } + + ~vec_binop_vecval_node() + { + memory_context_.clear(); + } + + inline T value() const exprtk_override + { + branch(0)->value(); + const T v = branch(1)->value(); + + const T* vec0 = vec0_node_ptr_->vds().data(); + T* vec1 = vds().data(); + + loop_unroll::details lud(size()); + const T* upper_bound = vec0 + lud.upper_bound; + + while (vec0 < upper_bound) + { + #define exprtk_loop(N) \ + vec1[N] = Operation::process(vec0[N], v); \ + + exprtk_loop( 0) exprtk_loop( 1) + exprtk_loop( 2) exprtk_loop( 3) + #ifndef exprtk_disable_superscalar_unroll + exprtk_loop( 4) exprtk_loop( 5) + exprtk_loop( 6) exprtk_loop( 7) + exprtk_loop( 8) exprtk_loop( 9) + exprtk_loop(10) exprtk_loop(11) + exprtk_loop(12) exprtk_loop(13) + exprtk_loop(14) exprtk_loop(15) + #endif + + vec0 += lud.batch_size; + vec1 += lud.batch_size; + } + + int i = 0; + + switch (lud.remainder) + { + #define case_stmt(N,fall_through) \ + case N : { vec1[i] = Operation::process(vec0[i], v); ++i; } \ + fall_through \ + + #ifndef exprtk_disable_superscalar_unroll + case_stmt(15, exprtk_fallthrough) case_stmt(14, exprtk_fallthrough) + case_stmt(13, exprtk_fallthrough) case_stmt(12, exprtk_fallthrough) + case_stmt(11, exprtk_fallthrough) case_stmt(10, exprtk_fallthrough) + case_stmt( 9, exprtk_fallthrough) case_stmt( 8, exprtk_fallthrough) + case_stmt( 7, exprtk_fallthrough) case_stmt( 6, exprtk_fallthrough) + case_stmt( 5, exprtk_fallthrough) case_stmt( 4, exprtk_fallthrough) + #endif + case_stmt( 3, exprtk_fallthrough) case_stmt( 2, exprtk_fallthrough) + case_stmt( 1, (void)0;) + } + + #undef exprtk_loop + #undef case_stmt + + return (vds().data())[0]; + } + + vector_node_ptr vec() const exprtk_override + { + return memory_context_.temp_vec_node_; + } + + vector_node_ptr vec() exprtk_override + { + return memory_context_.temp_vec_node_; + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_vecvalarith; + } + + inline bool valid() const exprtk_override + { + return + vec0_node_ptr_ && + (size() <= base_size()) && + binary_node::valid(); + } + + std::size_t size() const exprtk_override + { + return vec0_node_ptr_->size(); + } + + std::size_t base_size() const exprtk_override + { + return vec0_node_ptr_->vec_holder().base_size(); + } + + vds_t& vds() exprtk_override + { + return vds_; + } + + const vds_t& vds() const exprtk_override + { + return vds_; + } + + private: + + vector_node_ptr vec0_node_ptr_; + vds_t vds_; + memory_context memory_context_; + }; + + template + class vec_binop_valvec_node exprtk_final + : public binary_node + , public vector_interface + { + public: + + typedef expression_node* expression_ptr; + typedef vector_node* vector_node_ptr; + typedef vector_holder vector_holder_t; + typedef vector_holder_t* vector_holder_ptr; + typedef vec_data_store vds_t; + typedef memory_context_t memory_context; + + using binary_node::branch; + + vec_binop_valvec_node(const operator_type& opr, + expression_ptr branch0, + expression_ptr branch1) + : binary_node(opr, branch0, branch1) + , vec1_node_ptr_(0) + { + bool v1_is_ivec = false; + + if (is_vector_node(branch(1))) + { + vec1_node_ptr_ = static_cast(branch(1)); + } + else if (is_ivector_node(branch(1))) + { + vector_interface* vi = reinterpret_cast*>(0); + + if (0 != (vi = dynamic_cast*>(branch(1)))) + { + vec1_node_ptr_ = vi->vec(); + v1_is_ivec = true; + } + } + + if (vec1_node_ptr_) + { + if (v1_is_ivec) + vds() = vec1_node_ptr_->vds(); + else + vds() = vds_t(vec1_node_ptr_->base_size()); + + memory_context_ = make_memory_context(vec1_node_ptr_->vec_holder(), vds()); + } + + assert(valid()); + } + + ~vec_binop_valvec_node() + { + memory_context_.clear(); + } + + inline T value() const exprtk_override + { + const T v = branch(0)->value(); + branch(1)->value(); + + T* vec0 = vds().data(); + const T* vec1 = vec1_node_ptr_->vds().data(); + + loop_unroll::details lud(size()); + const T* upper_bound = vec0 + lud.upper_bound; + + while (vec0 < upper_bound) + { + #define exprtk_loop(N) \ + vec0[N] = Operation::process(v, vec1[N]); \ + + exprtk_loop( 0) exprtk_loop( 1) + exprtk_loop( 2) exprtk_loop( 3) + #ifndef exprtk_disable_superscalar_unroll + exprtk_loop( 4) exprtk_loop( 5) + exprtk_loop( 6) exprtk_loop( 7) + exprtk_loop( 8) exprtk_loop( 9) + exprtk_loop(10) exprtk_loop(11) + exprtk_loop(12) exprtk_loop(13) + exprtk_loop(14) exprtk_loop(15) + #endif + + vec0 += lud.batch_size; + vec1 += lud.batch_size; + } + + int i = 0; + + switch (lud.remainder) + { + #define case_stmt(N,fall_through) \ + case N : { vec0[i] = Operation::process(v, vec1[i]); ++i; } \ + fall_through \ + + #ifndef exprtk_disable_superscalar_unroll + case_stmt(15, exprtk_fallthrough) case_stmt(14, exprtk_fallthrough) + case_stmt(13, exprtk_fallthrough) case_stmt(12, exprtk_fallthrough) + case_stmt(11, exprtk_fallthrough) case_stmt(10, exprtk_fallthrough) + case_stmt( 9, exprtk_fallthrough) case_stmt( 8, exprtk_fallthrough) + case_stmt( 7, exprtk_fallthrough) case_stmt( 6, exprtk_fallthrough) + case_stmt( 5, exprtk_fallthrough) case_stmt( 4, exprtk_fallthrough) + #endif + case_stmt( 3, exprtk_fallthrough) case_stmt( 2, exprtk_fallthrough) + case_stmt( 1, (void)0;) + } + + #undef exprtk_loop + #undef case_stmt + + return (vds().data())[0]; + } + + vector_node_ptr vec() const exprtk_override + { + return memory_context_.temp_vec_node_; + } + + vector_node_ptr vec() exprtk_override + { + return memory_context_.temp_vec_node_; + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_vecvalarith; + } + + inline bool valid() const exprtk_override + { + return + vec1_node_ptr_ && + (size() <= base_size()) && + (vds_.size() <= base_size()) && + binary_node::valid(); + } + + std::size_t size() const exprtk_override + { + return vec1_node_ptr_->vec_holder().size(); + } + + std::size_t base_size() const exprtk_override + { + return vec1_node_ptr_->vec_holder().base_size(); + } + + vds_t& vds() exprtk_override + { + return vds_; + } + + const vds_t& vds() const exprtk_override + { + return vds_; + } + + private: + + vector_node_ptr vec1_node_ptr_; + vds_t vds_; + memory_context memory_context_; + }; + + template + class unary_vector_node exprtk_final + : public unary_node + , public vector_interface + { + public: + + typedef expression_node* expression_ptr; + typedef vector_node* vector_node_ptr; + typedef vector_holder vector_holder_t; + typedef vector_holder_t* vector_holder_ptr; + typedef vec_data_store vds_t; + typedef memory_context_t memory_context; + + using expression_node::branch; + + unary_vector_node(const operator_type& opr, expression_ptr branch0) + : unary_node(opr, branch0) + , vec0_node_ptr_(0) + { + bool vec0_is_ivec = false; + + if (is_vector_node(branch(0))) + { + vec0_node_ptr_ = static_cast(branch(0)); + } + else if (is_ivector_node(branch(0))) + { + vector_interface* vi = reinterpret_cast*>(0); + + if (0 != (vi = dynamic_cast*>(branch(0)))) + { + vec0_node_ptr_ = vi->vec(); + vec0_is_ivec = true; + } + } + + if (vec0_node_ptr_) + { + if (vec0_is_ivec) + vds_ = vec0_node_ptr_->vds(); + else + vds_ = vds_t(vec0_node_ptr_->base_size()); + + memory_context_ = make_memory_context(vec0_node_ptr_->vec_holder(), vds()); + } + + assert(valid()); + } + + ~unary_vector_node() + { + memory_context_.clear(); + } + + inline T value() const exprtk_override + { + branch()->value(); + + const T* vec0 = vec0_node_ptr_->vds().data(); + T* vec1 = vds().data(); + + loop_unroll::details lud(size()); + const T* upper_bound = vec0 + lud.upper_bound; + + while (vec0 < upper_bound) + { + #define exprtk_loop(N) \ + vec1[N] = Operation::process(vec0[N]); \ + + exprtk_loop( 0) exprtk_loop( 1) + exprtk_loop( 2) exprtk_loop( 3) + #ifndef exprtk_disable_superscalar_unroll + exprtk_loop( 4) exprtk_loop( 5) + exprtk_loop( 6) exprtk_loop( 7) + exprtk_loop( 8) exprtk_loop( 9) + exprtk_loop(10) exprtk_loop(11) + exprtk_loop(12) exprtk_loop(13) + exprtk_loop(14) exprtk_loop(15) + #endif + + vec0 += lud.batch_size; + vec1 += lud.batch_size; + } + + int i = 0; + + switch (lud.remainder) + { + #define case_stmt(N) \ + case N : { vec1[i] = Operation::process(vec0[i]); ++i; } \ + exprtk_fallthrough \ + + #ifndef exprtk_disable_superscalar_unroll + case_stmt(15) case_stmt(14) + case_stmt(13) case_stmt(12) + case_stmt(11) case_stmt(10) + case_stmt( 9) case_stmt( 8) + case_stmt( 7) case_stmt( 6) + case_stmt( 5) case_stmt( 4) + #endif + case_stmt( 3) case_stmt( 2) + case_stmt( 1) + default: break; + } + + #undef exprtk_loop + #undef case_stmt + + return (vds().data())[0]; + } + + vector_node_ptr vec() const exprtk_override + { + return memory_context_.temp_vec_node_; + } + + vector_node_ptr vec() exprtk_override + { + return memory_context_.temp_vec_node_; + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_vecunaryop; + } + + inline bool valid() const exprtk_override + { + return vec0_node_ptr_ && unary_node::valid(); + } + + std::size_t size() const exprtk_override + { + return vec0_node_ptr_->vec_holder().size(); + } + + std::size_t base_size() const exprtk_override + { + return vec0_node_ptr_->vec_holder().base_size(); + } + + vds_t& vds() exprtk_override + { + return vds_; + } + + const vds_t& vds() const exprtk_override + { + return vds_; + } + + private: + + vector_node_ptr vec0_node_ptr_; + vds_t vds_; + memory_context memory_context_; + }; + + template + class conditional_vector_node exprtk_final + : public expression_node + , public vector_interface + { + public: + + typedef expression_node * expression_ptr; + typedef vector_interface* vec_interface_ptr; + typedef vector_node * vector_node_ptr; + typedef vector_holder vector_holder_t; + typedef vector_holder_t* vector_holder_ptr; + typedef vec_data_store vds_t; + typedef memory_context_t memory_context; + typedef std::pair branch_t; + + conditional_vector_node(expression_ptr condition, + expression_ptr consequent, + expression_ptr alternative) + : consequent_node_ptr_ (0) + , alternative_node_ptr_(0) + , temp_vec_node_ (0) + , temp_ (0) + , result_vec_size_ (0) + , initialised_ (false) + { + construct_branch_pair(condition_ , condition ); + construct_branch_pair(consequent_ , consequent ); + construct_branch_pair(alternative_, alternative); + + if (details::is_ivector_node(consequent_.first)) + { + vec_interface_ptr ivec_ptr = dynamic_cast(consequent_.first); + + if (0 != ivec_ptr) + { + consequent_node_ptr_ = ivec_ptr->vec(); + } + } + + if (details::is_ivector_node(alternative_.first)) + { + vec_interface_ptr ivec_ptr = dynamic_cast(alternative_.first); + + if (0 != ivec_ptr) + { + alternative_node_ptr_ = ivec_ptr->vec(); + } + } + + if (consequent_node_ptr_ && alternative_node_ptr_) + { + const std::size_t vec_size = + std::max(consequent_node_ptr_ ->vec_holder().base_size(), + alternative_node_ptr_->vec_holder().base_size()); + + vds_ = vds_t(vec_size); + memory_context_ = make_memory_context( + consequent_node_ptr_ ->vec_holder(), + alternative_node_ptr_->vec_holder(), + vds()); + + initialised_ = (vec_size > 0); + } + + assert(initialised_); + } + + ~conditional_vector_node() + { + memory_context_.clear(); + } + + inline T value() const exprtk_override + { + T result = T(0); + T* source_vector = 0; + T* result_vector = vds().data(); + + if (is_true(condition_)) + { + result = consequent_.first->value(); + source_vector = consequent_node_ptr_->vds().data(); + result_vec_size_ = consequent_node_ptr_->size(); + } + else + { + result = alternative_.first->value(); + source_vector = alternative_node_ptr_->vds().data(); + result_vec_size_ = alternative_node_ptr_->size(); + } + + for (std::size_t i = 0; i < result_vec_size_; ++i) + { + result_vector[i] = source_vector[i]; + } + + return result; + } + + vector_node_ptr vec() const exprtk_override + { + return memory_context_.temp_vec_node_; + } + + vector_node_ptr vec() exprtk_override + { + return memory_context_.temp_vec_node_; + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_vecondition; + } + + inline bool valid() const exprtk_override + { + return + initialised_ && + condition_ .first && condition_ .first->valid() && + consequent_ .first && consequent_ .first->valid() && + alternative_.first && alternative_.first->valid() && + size() <= base_size(); + } + + std::size_t size() const exprtk_override + { + return result_vec_size_; + } + + std::size_t base_size() const exprtk_override + { + return std::min( + consequent_node_ptr_ ->vec_holder().base_size(), + alternative_node_ptr_->vec_holder().base_size()); + } + + vds_t& vds() exprtk_override + { + return vds_; + } + + const vds_t& vds() const exprtk_override + { + return vds_; + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(condition_ , node_delete_list); + expression_node::ndb_t::collect(consequent_ , node_delete_list); + expression_node::ndb_t::collect(alternative_ , node_delete_list); + } + + std::size_t node_depth() const exprtk_override + { + return expression_node::ndb_t::compute_node_depth + (condition_, consequent_, alternative_); + } + + private: + + branch_t condition_; + branch_t consequent_; + branch_t alternative_; + vector_node_ptr consequent_node_ptr_; + vector_node_ptr alternative_node_ptr_; + vector_node_ptr temp_vec_node_; + vector_holder_ptr temp_; + vds_t vds_; + mutable std::size_t result_vec_size_; + bool initialised_; + memory_context memory_context_; + }; + + template + class scand_node exprtk_final : public binary_node + { + public: + + typedef expression_node* expression_ptr; + using binary_node::branch; + + scand_node(const operator_type& opr, + expression_ptr branch0, + expression_ptr branch1) + : binary_node(opr, branch0, branch1) + { + assert(binary_node::valid()); + } + + inline T value() const exprtk_override + { + return ( + std::not_equal_to() + (T(0),branch(0)->value()) && + std::not_equal_to() + (T(0),branch(1)->value()) + ) ? T(1) : T(0); + } + }; + + template + class scor_node exprtk_final : public binary_node + { + public: + + typedef expression_node* expression_ptr; + using binary_node::branch; + + scor_node(const operator_type& opr, + expression_ptr branch0, + expression_ptr branch1) + : binary_node(opr, branch0, branch1) + { + assert(binary_node::valid()); + } + + inline T value() const exprtk_override + { + return ( + std::not_equal_to() + (T(0),branch(0)->value()) || + std::not_equal_to() + (T(0),branch(1)->value()) + ) ? T(1) : T(0); + } + }; + + template + class function_N_node exprtk_final : public expression_node + { + public: + + // Function of N parameters. + typedef expression_node* expression_ptr; + typedef std::pair branch_t; + typedef IFunction ifunction; + + explicit function_N_node(ifunction* func) + : function_((N == func->param_count) ? func : reinterpret_cast(0)) + , parameter_count_(func->param_count) + , initialised_(false) + {} + + template + bool init_branches(expression_ptr (&b)[NumBranches]) + { + // Needed for incompetent and broken msvc compiler versions + #ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable: 4127) + #endif + + if (N != NumBranches) + { + return false; + } + + for (std::size_t i = 0; i < NumBranches; ++i) + { + if (b[i] && b[i]->valid()) + branch_[i] = std::make_pair(b[i],branch_deletable(b[i])); + else + return false; + } + + initialised_ = function_; + assert(valid()); + return initialised_; + + #ifdef _MSC_VER + #pragma warning(pop) + #endif + } + + inline bool operator <(const function_N_node& fn) const + { + return this < (&fn); + } + + inline T value() const exprtk_override + { + // Needed for incompetent and broken msvc compiler versions + #ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable: 4127) + #endif + + T v[N]; + evaluate_branches::execute(v,branch_); + return invoke::execute(*function_,v); + + #ifdef _MSC_VER + #pragma warning(pop) + #endif + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_function; + } + + inline bool valid() const exprtk_override + { + return initialised_; + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(branch_, node_delete_list); + } + + std::size_t node_depth() const exprtk_override + { + return expression_node::ndb_t::template compute_node_depth(branch_); + } + + template + struct evaluate_branches + { + static inline void execute(T_ (&v)[BranchCount], const branch_t (&b)[BranchCount]) + { + for (std::size_t i = 0; i < BranchCount; ++i) + { + v[i] = b[i].first->value(); + } + } + }; + + template + struct evaluate_branches + { + static inline void execute(T_ (&v)[6], const branch_t (&b)[6]) + { + v[0] = b[0].first->value(); + v[1] = b[1].first->value(); + v[2] = b[2].first->value(); + v[3] = b[3].first->value(); + v[4] = b[4].first->value(); + v[5] = b[5].first->value(); + } + }; + + template + struct evaluate_branches + { + static inline void execute(T_ (&v)[5], const branch_t (&b)[5]) + { + v[0] = b[0].first->value(); + v[1] = b[1].first->value(); + v[2] = b[2].first->value(); + v[3] = b[3].first->value(); + v[4] = b[4].first->value(); + } + }; + + template + struct evaluate_branches + { + static inline void execute(T_ (&v)[4], const branch_t (&b)[4]) + { + v[0] = b[0].first->value(); + v[1] = b[1].first->value(); + v[2] = b[2].first->value(); + v[3] = b[3].first->value(); + } + }; + + template + struct evaluate_branches + { + static inline void execute(T_ (&v)[3], const branch_t (&b)[3]) + { + v[0] = b[0].first->value(); + v[1] = b[1].first->value(); + v[2] = b[2].first->value(); + } + }; + + template + struct evaluate_branches + { + static inline void execute(T_ (&v)[2], const branch_t (&b)[2]) + { + v[0] = b[0].first->value(); + v[1] = b[1].first->value(); + } + }; + + template + struct evaluate_branches + { + static inline void execute(T_ (&v)[1], const branch_t (&b)[1]) + { + v[0] = b[0].first->value(); + } + }; + + template + struct invoke { static inline T execute(ifunction&, branch_t (&)[ParamCount]) { return std::numeric_limits::quiet_NaN(); } }; + + template + struct invoke + { + static inline T_ execute(ifunction& f, T_ (&v)[20]) + { return f(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7],v[8],v[9],v[10],v[11],v[12],v[13],v[14],v[15],v[16],v[17],v[18],v[19]); } + }; + + template + struct invoke + { + static inline T_ execute(ifunction& f, T_ (&v)[19]) + { return f(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7],v[8],v[9],v[10],v[11],v[12],v[13],v[14],v[15],v[16],v[17],v[18]); } + }; + + template + struct invoke + { + static inline T_ execute(ifunction& f, T_ (&v)[18]) + { return f(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8], v[9], v[10], v[11], v[12], v[13], v[14], v[15], v[16], v[17]); } + }; + + template + struct invoke + { + static inline T_ execute(ifunction& f, T_ (&v)[17]) + { return f(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8], v[9], v[10], v[11], v[12], v[13], v[14], v[15], v[16]); } + }; + + template + struct invoke + { + static inline T_ execute(ifunction& f, T_ (&v)[16]) + { return f(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8], v[9], v[10], v[11], v[12], v[13], v[14], v[15]); } + }; + + template + struct invoke + { + static inline T_ execute(ifunction& f, T_ (&v)[15]) + { return f(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8], v[9], v[10], v[11], v[12], v[13], v[14]); } + }; + + template + struct invoke + { + static inline T_ execute(ifunction& f, T_ (&v)[14]) + { return f(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8], v[9], v[10], v[11], v[12], v[13]); } + }; + + template + struct invoke + { + static inline T_ execute(ifunction& f, T_ (&v)[13]) + { return f(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8], v[9], v[10], v[11], v[12]); } + }; + + template + struct invoke + { + static inline T_ execute(ifunction& f, T_ (&v)[12]) + { return f(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8], v[9], v[10], v[11]); } + }; + + template + struct invoke + { + static inline T_ execute(ifunction& f, T_ (&v)[11]) + { return f(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8], v[9], v[10]); } + }; + + template + struct invoke + { + static inline T_ execute(ifunction& f, T_ (&v)[10]) + { return f(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8], v[9]); } + }; + + template + struct invoke + { + static inline T_ execute(ifunction& f, T_ (&v)[9]) + { return f(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8]); } + }; + + template + struct invoke + { + static inline T_ execute(ifunction& f, T_ (&v)[8]) + { return f(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]); } + }; + + template + struct invoke + { + static inline T_ execute(ifunction& f, T_ (&v)[7]) + { return f(v[0], v[1], v[2], v[3], v[4], v[5], v[6]); } + }; + + template + struct invoke + { + static inline T_ execute(ifunction& f, T_ (&v)[6]) + { return f(v[0], v[1], v[2], v[3], v[4], v[5]); } + }; + + template + struct invoke + { + static inline T_ execute(ifunction& f, T_ (&v)[5]) + { return f(v[0], v[1], v[2], v[3], v[4]); } + }; + + template + struct invoke + { + static inline T_ execute(ifunction& f, T_ (&v)[4]) + { return f(v[0], v[1], v[2], v[3]); } + }; + + template + struct invoke + { + static inline T_ execute(ifunction& f, T_ (&v)[3]) + { return f(v[0], v[1], v[2]); } + }; + + template + struct invoke + { + static inline T_ execute(ifunction& f, T_ (&v)[2]) + { return f(v[0], v[1]); } + }; + + template + struct invoke + { + static inline T_ execute(ifunction& f, T_ (&v)[1]) + { return f(v[0]); } + }; + + private: + + ifunction* function_; + std::size_t parameter_count_; + branch_t branch_[N]; + bool initialised_; + }; + + template + class function_N_node exprtk_final : public expression_node + { + public: + + typedef expression_node* expression_ptr; + typedef IFunction ifunction; + + explicit function_N_node(ifunction* func) + : function_((0 == func->param_count) ? func : reinterpret_cast(0)) + { + assert(valid()); + } + + inline bool operator <(const function_N_node& fn) const + { + return this < (&fn); + } + + inline T value() const exprtk_override + { + return (*function_)(); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_function; + } + + inline bool valid() const exprtk_override + { + return function_; + } + + private: + + ifunction* function_; + }; + + template + class vararg_function_node exprtk_final : public expression_node + { + public: + + typedef expression_node* expression_ptr; + + vararg_function_node(VarArgFunction* func, + const std::vector& arg_list) + : function_(func) + , arg_list_(arg_list) + { + value_list_.resize(arg_list.size(),std::numeric_limits::quiet_NaN()); + assert(valid()); + } + + inline bool operator <(const vararg_function_node& fn) const + { + return this < (&fn); + } + + inline T value() const exprtk_override + { + populate_value_list(); + return (*function_)(value_list_); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_vafunction; + } + + inline bool valid() const exprtk_override + { + return function_; + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + for (std::size_t i = 0; i < arg_list_.size(); ++i) + { + if (arg_list_[i] && !details::is_variable_node(arg_list_[i])) + { + node_delete_list.push_back(&arg_list_[i]); + } + } + } + + std::size_t node_depth() const exprtk_override + { + return expression_node::ndb_t::compute_node_depth(arg_list_); + } + + private: + + inline void populate_value_list() const + { + for (std::size_t i = 0; i < arg_list_.size(); ++i) + { + value_list_[i] = arg_list_[i]->value(); + } + } + + VarArgFunction* function_; + std::vector arg_list_; + mutable std::vector value_list_; + }; + + template + class generic_function_node : public expression_node + { + public: + + typedef type_store type_store_t; + typedef expression_node* expression_ptr; + typedef variable_node variable_node_t; + typedef vector_node vector_node_t; + typedef variable_node_t* variable_node_ptr_t; + typedef vector_node_t* vector_node_ptr_t; + typedef range_interface range_interface_t; + typedef range_data_type range_data_type_t; + typedef typename range_interface::range_t range_t; + + typedef std::pair branch_t; + typedef vector_holder* vh_t; + typedef vector_view* vecview_t; + + typedef std::vector tmp_vs_t; + typedef std::vector typestore_list_t; + typedef std::vector range_list_t; + + explicit generic_function_node(const std::vector& arg_list, + GenericFunction* func = reinterpret_cast(0)) + : function_(func) + , arg_list_(arg_list) + {} + + virtual ~generic_function_node() + { + for (std::size_t i = 0; i < vv_list_.size(); ++i) + { + vecview_t& vv = vv_list_[i]; + if (vv && typestore_list_[i].vec_data) + { + vv->remove_ref(&typestore_list_[i].vec_data); + typestore_list_[i].vec_data = 0; + } + } + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(branch_, node_delete_list); + } + + std::size_t node_depth() const exprtk_override exprtk_final + { + return expression_node::ndb_t::compute_node_depth(branch_); + } + + virtual bool init_branches() + { + expr_as_vec1_store_.resize(arg_list_.size(), T(0) ); + typestore_list_ .resize(arg_list_.size(), type_store_t() ); + range_list_ .resize(arg_list_.size(), range_data_type_t()); + branch_ .resize(arg_list_.size(), branch_t(reinterpret_cast(0),false)); + vv_list_ .resize(arg_list_.size(), vecview_t(0)); + + for (std::size_t i = 0; i < arg_list_.size(); ++i) + { + type_store_t& ts = typestore_list_[i]; + + if (0 == arg_list_[i]) + return false; + else if (is_ivector_node(arg_list_[i])) + { + vector_interface* vi = reinterpret_cast*>(0); + + if (0 == (vi = dynamic_cast*>(arg_list_[i]))) + return false; + + ts.size = vi->size(); + ts.data = vi->vds().data(); + ts.type = type_store_t::e_vector; + + if ( + vi->vec()->vec_holder().rebaseable() && + vi->vec()->vec_holder().rebaseable_instance() + ) + { + vv_list_[i] = vi->vec()->vec_holder().rebaseable_instance(); + vv_list_[i]->set_ref(&ts.vec_data); + } + } + #ifndef exprtk_disable_string_capabilities + else if (is_generally_string_node(arg_list_[i])) + { + string_base_node* sbn = reinterpret_cast*>(0); + + if (0 == (sbn = dynamic_cast*>(arg_list_[i]))) + return false; + + ts.size = sbn->size(); + ts.data = reinterpret_cast(const_cast(sbn->base())); + ts.type = type_store_t::e_string; + + range_list_[i].data = ts.data; + range_list_[i].size = ts.size; + range_list_[i].type_size = sizeof(char); + range_list_[i].str_node = sbn; + + range_interface_t* ri = reinterpret_cast(0); + + if (0 == (ri = dynamic_cast(arg_list_[i]))) + return false; + + const range_t& rp = ri->range_ref(); + + if ( + rp.const_range() && + is_const_string_range_node(arg_list_[i]) + ) + { + ts.size = rp.const_size(); + ts.data = static_cast(ts.data) + rp.n0_c.second; + range_list_[i].range = reinterpret_cast(0); + } + else + { + range_list_[i].range = &(ri->range_ref()); + range_param_list_.push_back(i); + } + } + #endif + else if (is_variable_node(arg_list_[i])) + { + variable_node_ptr_t var = variable_node_ptr_t(0); + + if (0 == (var = dynamic_cast(arg_list_[i]))) + return false; + + ts.size = 1; + ts.data = &var->ref(); + ts.type = type_store_t::e_scalar; + } + else + { + ts.size = 1; + ts.data = reinterpret_cast(&expr_as_vec1_store_[i]); + ts.type = type_store_t::e_scalar; + } + + branch_[i] = std::make_pair(arg_list_[i],branch_deletable(arg_list_[i])); + } + + return true; + } + + inline bool operator <(const generic_function_node& fn) const + { + return this < (&fn); + } + + inline T value() const exprtk_override + { + if (populate_value_list()) + { + typedef typename GenericFunction::parameter_list_t parameter_list_t; + + return (*function_)(parameter_list_t(typestore_list_)); + } + + return std::numeric_limits::quiet_NaN(); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_genfunction; + } + + inline bool valid() const exprtk_override + { + return function_; + } + + protected: + + inline virtual bool populate_value_list() const + { + for (std::size_t i = 0; i < branch_.size(); ++i) + { + expr_as_vec1_store_[i] = branch_[i].first->value(); + } + + if (!range_param_list_.empty()) + { + assert(range_param_list_.size() <= branch_.size()); + + for (std::size_t i = 0; i < range_param_list_.size(); ++i) + { + const std::size_t index = range_param_list_[i]; + range_data_type_t& rdt = range_list_[index]; + + const range_t& rp = (*rdt.range); + std::size_t r0 = 0; + std::size_t r1 = 0; + + const std::size_t data_size = + #ifndef exprtk_disable_string_capabilities + rdt.str_node ? rdt.str_node->size() : rdt.size; + #else + rdt.size; + #endif + + if (!rp(r0, r1, data_size)) + { + return false; + } + + type_store_t& ts = typestore_list_[index]; + + ts.size = rp.cache_size(); + #ifndef exprtk_disable_string_capabilities + if (ts.type == type_store_t::e_string) + ts.data = const_cast(rdt.str_node->base()) + rp.cache.first; + else + #endif + ts.data = static_cast(rdt.data) + (rp.cache.first * rdt.type_size); + } + } + + return true; + } + + GenericFunction* function_; + mutable typestore_list_t typestore_list_; + + private: + + std::vector arg_list_; + std::vector branch_; + std::vector vv_list_; + mutable tmp_vs_t expr_as_vec1_store_; + mutable range_list_t range_list_; + std::vector range_param_list_; + }; + + #ifndef exprtk_disable_string_capabilities + template + class string_function_node : public generic_function_node + , public string_base_node + , public range_interface + { + public: + + typedef generic_function_node gen_function_t; + typedef typename range_interface::range_t range_t; + + string_function_node(StringFunction* func, + const std::vector& arg_list) + : gen_function_t(arg_list,func) + { + range_.n0_c = std::make_pair(true,0); + range_.n1_c = std::make_pair(true,0); + range_.cache.first = range_.n0_c.second; + range_.cache.second = range_.n1_c.second; + assert(valid()); + } + + inline bool operator <(const string_function_node& fn) const + { + return this < (&fn); + } + + inline T value() const exprtk_override + { + if (gen_function_t::populate_value_list()) + { + typedef typename StringFunction::parameter_list_t parameter_list_t; + + const T result = + (*gen_function_t::function_) + ( + ret_string_, + parameter_list_t(gen_function_t::typestore_list_) + ); + + range_.n1_c.second = ret_string_.size(); + range_.cache.second = range_.n1_c.second; + + return result; + } + + return std::numeric_limits::quiet_NaN(); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_strfunction; + } + + inline bool valid() const exprtk_override + { + return gen_function_t::function_; + } + + std::string str() const exprtk_override + { + return ret_string_; + } + + char_cptr base() const exprtk_override + { + return &ret_string_[0]; + } + + std::size_t size() const exprtk_override + { + return ret_string_.size(); + } + + range_t& range_ref() exprtk_override + { + return range_; + } + + const range_t& range_ref() const exprtk_override + { + return range_; + } + + protected: + + mutable range_t range_; + mutable std::string ret_string_; + }; + #endif + + template + class multimode_genfunction_node : public generic_function_node + { + public: + + typedef generic_function_node gen_function_t; + typedef typename gen_function_t::range_t range_t; + + multimode_genfunction_node(GenericFunction* func, + const std::size_t& param_seq_index, + const std::vector& arg_list) + : gen_function_t(arg_list,func) + , param_seq_index_(param_seq_index) + {} + + inline T value() const exprtk_override + { + assert(gen_function_t::valid()); + + if (gen_function_t::populate_value_list()) + { + typedef typename GenericFunction::parameter_list_t parameter_list_t; + + return + (*gen_function_t::function_) + ( + param_seq_index_, + parameter_list_t(gen_function_t::typestore_list_) + ); + } + + return std::numeric_limits::quiet_NaN(); + } + + inline typename expression_node::node_type type() const exprtk_override exprtk_final + { + return expression_node::e_genfunction; + } + + private: + + std::size_t param_seq_index_; + }; + + #ifndef exprtk_disable_string_capabilities + template + class multimode_strfunction_node exprtk_final : public string_function_node + { + public: + + typedef string_function_node str_function_t; + typedef typename str_function_t::range_t range_t; + + multimode_strfunction_node(StringFunction* func, + const std::size_t& param_seq_index, + const std::vector& arg_list) + : str_function_t(func,arg_list) + , param_seq_index_(param_seq_index) + {} + + inline T value() const exprtk_override + { + if (str_function_t::populate_value_list()) + { + typedef typename StringFunction::parameter_list_t parameter_list_t; + + const T result = + (*str_function_t::function_) + ( + param_seq_index_, + str_function_t::ret_string_, + parameter_list_t(str_function_t::typestore_list_) + ); + + str_function_t::range_.n1_c.second = str_function_t::ret_string_.size(); + str_function_t::range_.cache.second = str_function_t::range_.n1_c.second; + + return result; + } + + return std::numeric_limits::quiet_NaN(); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_strfunction; + } + + private: + + const std::size_t param_seq_index_; + }; + #endif + + class return_exception {}; + + template + class null_igenfunc + { + public: + + virtual ~null_igenfunc() + {} + + typedef type_store generic_type; + typedef typename generic_type::parameter_list parameter_list_t; + + inline virtual T operator() (parameter_list_t) + { + return std::numeric_limits::quiet_NaN(); + } + }; + + #ifndef exprtk_disable_return_statement + template + class return_node exprtk_final : public generic_function_node > + { + public: + + typedef results_context results_context_t; + typedef null_igenfunc igeneric_function_t; + typedef igeneric_function_t* igeneric_function_ptr; + typedef generic_function_node gen_function_t; + + return_node(const std::vector& arg_list, + results_context_t& rc) + : gen_function_t (arg_list) + , results_context_(&rc) + { + assert(valid()); + } + + inline T value() const exprtk_override + { + if (gen_function_t::populate_value_list()) + { + typedef typename type_store::parameter_list parameter_list_t; + + results_context_-> + assign(parameter_list_t(gen_function_t::typestore_list_)); + + throw return_exception(); + } + + return std::numeric_limits::quiet_NaN(); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_return; + } + + inline bool valid() const exprtk_override + { + return results_context_; + } + + private: + + results_context_t* results_context_; + }; + + template + class return_envelope_node exprtk_final : public expression_node + { + public: + + typedef expression_node* expression_ptr; + typedef results_context results_context_t; + typedef std::pair branch_t; + + return_envelope_node(expression_ptr body, results_context_t& rc) + : results_context_(&rc ) + , return_invoked_ (false) + { + construct_branch_pair(body_, body); + assert(valid()); + } + + inline T value() const exprtk_override + { + try + { + return_invoked_ = false; + results_context_->clear(); + + return body_.first->value(); + } + catch(const return_exception&) + { + return_invoked_ = true; + + return std::numeric_limits::quiet_NaN(); + } + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_retenv; + } + + inline bool valid() const exprtk_override + { + return results_context_ && body_.first; + } + + inline bool* retinvk_ptr() + { + return &return_invoked_; + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(body_, node_delete_list); + } + + std::size_t node_depth() const exprtk_override + { + return expression_node::ndb_t::compute_node_depth(body_); + } + + private: + + results_context_t* results_context_; + mutable bool return_invoked_; + branch_t body_; + }; + #endif + + #define exprtk_define_unary_op(OpName) \ + template \ + struct OpName##_op \ + { \ + typedef typename functor_t::Type Type; \ + typedef typename expression_node::node_type node_t; \ + \ + static inline T process(Type v) \ + { \ + return numeric:: OpName (v); \ + } \ + \ + static inline node_t type() \ + { \ + return expression_node::e_##OpName; \ + } \ + \ + static inline details::operator_type operation() \ + { \ + return details::e_##OpName; \ + } \ + }; \ + + exprtk_define_unary_op(abs ) + exprtk_define_unary_op(acos ) + exprtk_define_unary_op(acosh) + exprtk_define_unary_op(asin ) + exprtk_define_unary_op(asinh) + exprtk_define_unary_op(atan ) + exprtk_define_unary_op(atanh) + exprtk_define_unary_op(ceil ) + exprtk_define_unary_op(cos ) + exprtk_define_unary_op(cosh ) + exprtk_define_unary_op(cot ) + exprtk_define_unary_op(csc ) + exprtk_define_unary_op(d2g ) + exprtk_define_unary_op(d2r ) + exprtk_define_unary_op(erf ) + exprtk_define_unary_op(erfc ) + exprtk_define_unary_op(exp ) + exprtk_define_unary_op(expm1) + exprtk_define_unary_op(floor) + exprtk_define_unary_op(frac ) + exprtk_define_unary_op(g2d ) + exprtk_define_unary_op(log ) + exprtk_define_unary_op(log10) + exprtk_define_unary_op(log2 ) + exprtk_define_unary_op(log1p) + exprtk_define_unary_op(ncdf ) + exprtk_define_unary_op(neg ) + exprtk_define_unary_op(notl ) + exprtk_define_unary_op(pos ) + exprtk_define_unary_op(r2d ) + exprtk_define_unary_op(round) + exprtk_define_unary_op(sec ) + exprtk_define_unary_op(sgn ) + exprtk_define_unary_op(sin ) + exprtk_define_unary_op(sinc ) + exprtk_define_unary_op(sinh ) + exprtk_define_unary_op(sqrt ) + exprtk_define_unary_op(tan ) + exprtk_define_unary_op(tanh ) + exprtk_define_unary_op(trunc) + #undef exprtk_define_unary_op + + template + struct opr_base + { + typedef typename details::functor_t::Type Type; + typedef typename details::functor_t::RefType RefType; + typedef typename details::functor_t functor_t; + typedef typename functor_t::qfunc_t quaternary_functor_t; + typedef typename functor_t::tfunc_t trinary_functor_t; + typedef typename functor_t::bfunc_t binary_functor_t; + typedef typename functor_t::ufunc_t unary_functor_t; + }; + + template + struct add_op : public opr_base + { + typedef typename opr_base::Type Type; + typedef typename opr_base::RefType RefType; + + static inline T process(Type t1, Type t2) { return t1 + t2; } + static inline T process(Type t1, Type t2, Type t3) { return t1 + t2 + t3; } + static inline void assign(RefType t1, Type t2) { t1 += t2; } + static inline typename expression_node::node_type type() { return expression_node::e_add; } + static inline details::operator_type operation() { return details::e_add; } + }; + + template + struct mul_op : public opr_base + { + typedef typename opr_base::Type Type; + typedef typename opr_base::RefType RefType; + + static inline T process(Type t1, Type t2) { return t1 * t2; } + static inline T process(Type t1, Type t2, Type t3) { return t1 * t2 * t3; } + static inline void assign(RefType t1, Type t2) { t1 *= t2; } + static inline typename expression_node::node_type type() { return expression_node::e_mul; } + static inline details::operator_type operation() { return details::e_mul; } + }; + + template + struct sub_op : public opr_base + { + typedef typename opr_base::Type Type; + typedef typename opr_base::RefType RefType; + + static inline T process(Type t1, Type t2) { return t1 - t2; } + static inline T process(Type t1, Type t2, Type t3) { return t1 - t2 - t3; } + static inline void assign(RefType t1, Type t2) { t1 -= t2; } + static inline typename expression_node::node_type type() { return expression_node::e_sub; } + static inline details::operator_type operation() { return details::e_sub; } + }; + + template + struct div_op : public opr_base + { + typedef typename opr_base::Type Type; + typedef typename opr_base::RefType RefType; + + static inline T process(Type t1, Type t2) { return t1 / t2; } + static inline T process(Type t1, Type t2, Type t3) { return t1 / t2 / t3; } + static inline void assign(RefType t1, Type t2) { t1 /= t2; } + static inline typename expression_node::node_type type() { return expression_node::e_div; } + static inline details::operator_type operation() { return details::e_div; } + }; + + template + struct mod_op : public opr_base + { + typedef typename opr_base::Type Type; + typedef typename opr_base::RefType RefType; + + static inline T process(Type t1, Type t2) { return numeric::modulus(t1,t2); } + static inline void assign(RefType t1, Type t2) { t1 = numeric::modulus(t1,t2); } + static inline typename expression_node::node_type type() { return expression_node::e_mod; } + static inline details::operator_type operation() { return details::e_mod; } + }; + + template + struct pow_op : public opr_base + { + typedef typename opr_base::Type Type; + typedef typename opr_base::RefType RefType; + + static inline T process(Type t1, Type t2) { return numeric::pow(t1,t2); } + static inline void assign(RefType t1, Type t2) { t1 = numeric::pow(t1,t2); } + static inline typename expression_node::node_type type() { return expression_node::e_pow; } + static inline details::operator_type operation() { return details::e_pow; } + }; + + template + struct lt_op : public opr_base + { + typedef typename opr_base::Type Type; + + static inline T process(Type t1, Type t2) { return ((t1 < t2) ? T(1) : T(0)); } + static inline T process(const std::string& t1, const std::string& t2) { return ((t1 < t2) ? T(1) : T(0)); } + static inline typename expression_node::node_type type() { return expression_node::e_lt; } + static inline details::operator_type operation() { return details::e_lt; } + }; + + template + struct lte_op : public opr_base + { + typedef typename opr_base::Type Type; + + static inline T process(Type t1, Type t2) { return ((t1 <= t2) ? T(1) : T(0)); } + static inline T process(const std::string& t1, const std::string& t2) { return ((t1 <= t2) ? T(1) : T(0)); } + static inline typename expression_node::node_type type() { return expression_node::e_lte; } + static inline details::operator_type operation() { return details::e_lte; } + }; + + template + struct gt_op : public opr_base + { + typedef typename opr_base::Type Type; + + static inline T process(Type t1, Type t2) { return ((t1 > t2) ? T(1) : T(0)); } + static inline T process(const std::string& t1, const std::string& t2) { return ((t1 > t2) ? T(1) : T(0)); } + static inline typename expression_node::node_type type() { return expression_node::e_gt; } + static inline details::operator_type operation() { return details::e_gt; } + }; + + template + struct gte_op : public opr_base + { + typedef typename opr_base::Type Type; + + static inline T process(Type t1, Type t2) { return ((t1 >= t2) ? T(1) : T(0)); } + static inline T process(const std::string& t1, const std::string& t2) { return ((t1 >= t2) ? T(1) : T(0)); } + static inline typename expression_node::node_type type() { return expression_node::e_gte; } + static inline details::operator_type operation() { return details::e_gte; } + }; + + template + struct eq_op : public opr_base + { + typedef typename opr_base::Type Type; + static inline T process(Type t1, Type t2) { return (std::equal_to()(t1,t2) ? T(1) : T(0)); } + static inline T process(const std::string& t1, const std::string& t2) { return ((t1 == t2) ? T(1) : T(0)); } + static inline typename expression_node::node_type type() { return expression_node::e_eq; } + static inline details::operator_type operation() { return details::e_eq; } + }; + + template + struct equal_op : public opr_base + { + typedef typename opr_base::Type Type; + + static inline T process(Type t1, Type t2) { return numeric::equal(t1,t2); } + static inline T process(const std::string& t1, const std::string& t2) { return ((t1 == t2) ? T(1) : T(0)); } + static inline typename expression_node::node_type type() { return expression_node::e_eq; } + static inline details::operator_type operation() { return details::e_equal; } + }; + + template + struct ne_op : public opr_base + { + typedef typename opr_base::Type Type; + + static inline T process(Type t1, Type t2) { return (std::not_equal_to()(t1,t2) ? T(1) : T(0)); } + static inline T process(const std::string& t1, const std::string& t2) { return ((t1 != t2) ? T(1) : T(0)); } + static inline typename expression_node::node_type type() { return expression_node::e_ne; } + static inline details::operator_type operation() { return details::e_ne; } + }; + + template + struct and_op : public opr_base + { + typedef typename opr_base::Type Type; + + static inline T process(Type t1, Type t2) { return (details::is_true(t1) && details::is_true(t2)) ? T(1) : T(0); } + static inline typename expression_node::node_type type() { return expression_node::e_and; } + static inline details::operator_type operation() { return details::e_and; } + }; + + template + struct nand_op : public opr_base + { + typedef typename opr_base::Type Type; + + static inline T process(Type t1, Type t2) { return (details::is_true(t1) && details::is_true(t2)) ? T(0) : T(1); } + static inline typename expression_node::node_type type() { return expression_node::e_nand; } + static inline details::operator_type operation() { return details::e_nand; } + }; + + template + struct or_op : public opr_base + { + typedef typename opr_base::Type Type; + + static inline T process(Type t1, Type t2) { return (details::is_true(t1) || details::is_true(t2)) ? T(1) : T(0); } + static inline typename expression_node::node_type type() { return expression_node::e_or; } + static inline details::operator_type operation() { return details::e_or; } + }; + + template + struct nor_op : public opr_base + { + typedef typename opr_base::Type Type; + + static inline T process(Type t1, Type t2) { return (details::is_true(t1) || details::is_true(t2)) ? T(0) : T(1); } + static inline typename expression_node::node_type type() { return expression_node::e_nor; } + static inline details::operator_type operation() { return details::e_nor; } + }; + + template + struct xor_op : public opr_base + { + typedef typename opr_base::Type Type; + + static inline T process(Type t1, Type t2) { return numeric::xor_opr(t1,t2); } + static inline typename expression_node::node_type type() { return expression_node::e_nor; } + static inline details::operator_type operation() { return details::e_xor; } + }; + + template + struct xnor_op : public opr_base + { + typedef typename opr_base::Type Type; + + static inline T process(Type t1, Type t2) { return numeric::xnor_opr(t1,t2); } + static inline typename expression_node::node_type type() { return expression_node::e_nor; } + static inline details::operator_type operation() { return details::e_xnor; } + }; + + template + struct in_op : public opr_base + { + typedef typename opr_base::Type Type; + + static inline T process(const T&, const T&) { return std::numeric_limits::quiet_NaN(); } + static inline T process(const std::string& t1, const std::string& t2) { return ((std::string::npos != t2.find(t1)) ? T(1) : T(0)); } + static inline typename expression_node::node_type type() { return expression_node::e_in; } + static inline details::operator_type operation() { return details::e_in; } + }; + + template + struct like_op : public opr_base + { + typedef typename opr_base::Type Type; + + static inline T process(const T&, const T&) { return std::numeric_limits::quiet_NaN(); } + static inline T process(const std::string& t1, const std::string& t2) { return (details::wc_match(t2,t1) ? T(1) : T(0)); } + static inline typename expression_node::node_type type() { return expression_node::e_like; } + static inline details::operator_type operation() { return details::e_like; } + }; + + template + struct ilike_op : public opr_base + { + typedef typename opr_base::Type Type; + + static inline T process(const T&, const T&) { return std::numeric_limits::quiet_NaN(); } + static inline T process(const std::string& t1, const std::string& t2) { return (details::wc_imatch(t2,t1) ? T(1) : T(0)); } + static inline typename expression_node::node_type type() { return expression_node::e_ilike; } + static inline details::operator_type operation() { return details::e_ilike; } + }; + + template + struct inrange_op : public opr_base + { + typedef typename opr_base::Type Type; + + static inline T process(const T& t0, const T& t1, const T& t2) { return ((t0 <= t1) && (t1 <= t2)) ? T(1) : T(0); } + static inline T process(const std::string& t0, const std::string& t1, const std::string& t2) + { + return ((t0 <= t1) && (t1 <= t2)) ? T(1) : T(0); + } + static inline typename expression_node::node_type type() { return expression_node::e_inranges; } + static inline details::operator_type operation() { return details::e_inrange; } + }; + + template + inline T value(details::expression_node* n) + { + return n->value(); + } + + template + inline T value(std::pair*,bool> n) + { + return n.first->value(); + } + + template + inline T value(const T* t) + { + return (*t); + } + + template + inline T value(const T& t) + { + return t; + } + + template + struct vararg_add_op exprtk_final : public opr_base + { + typedef typename opr_base::Type Type; + + template class Sequence> + static inline T process(const Sequence& arg_list) + { + switch (arg_list.size()) + { + case 0 : return T(0); + case 1 : return process_1(arg_list); + case 2 : return process_2(arg_list); + case 3 : return process_3(arg_list); + case 4 : return process_4(arg_list); + case 5 : return process_5(arg_list); + default : + { + T result = T(0); + + for (std::size_t i = 0; i < arg_list.size(); ++i) + { + result += value(arg_list[i]); + } + + return result; + } + } + } + + template + static inline T process_1(const Sequence& arg_list) + { + return value(arg_list[0]); + } + + template + static inline T process_2(const Sequence& arg_list) + { + return value(arg_list[0]) + value(arg_list[1]); + } + + template + static inline T process_3(const Sequence& arg_list) + { + return value(arg_list[0]) + value(arg_list[1]) + + value(arg_list[2]) ; + } + + template + static inline T process_4(const Sequence& arg_list) + { + return value(arg_list[0]) + value(arg_list[1]) + + value(arg_list[2]) + value(arg_list[3]) ; + } + + template + static inline T process_5(const Sequence& arg_list) + { + return value(arg_list[0]) + value(arg_list[1]) + + value(arg_list[2]) + value(arg_list[3]) + + value(arg_list[4]) ; + } + }; + + template + struct vararg_mul_op exprtk_final : public opr_base + { + typedef typename opr_base::Type Type; + + template class Sequence> + static inline T process(const Sequence& arg_list) + { + switch (arg_list.size()) + { + case 0 : return T(0); + case 1 : return process_1(arg_list); + case 2 : return process_2(arg_list); + case 3 : return process_3(arg_list); + case 4 : return process_4(arg_list); + case 5 : return process_5(arg_list); + default : + { + T result = T(value(arg_list[0])); + + for (std::size_t i = 1; i < arg_list.size(); ++i) + { + result *= value(arg_list[i]); + } + + return result; + } + } + } + + template + static inline T process_1(const Sequence& arg_list) + { + return value(arg_list[0]); + } + + template + static inline T process_2(const Sequence& arg_list) + { + return value(arg_list[0]) * value(arg_list[1]); + } + + template + static inline T process_3(const Sequence& arg_list) + { + return value(arg_list[0]) * value(arg_list[1]) * + value(arg_list[2]) ; + } + + template + static inline T process_4(const Sequence& arg_list) + { + return value(arg_list[0]) * value(arg_list[1]) * + value(arg_list[2]) * value(arg_list[3]) ; + } + + template + static inline T process_5(const Sequence& arg_list) + { + return value(arg_list[0]) * value(arg_list[1]) * + value(arg_list[2]) * value(arg_list[3]) * + value(arg_list[4]) ; + } + }; + + template + struct vararg_avg_op exprtk_final : public opr_base + { + typedef typename opr_base::Type Type; + + template class Sequence> + static inline T process(const Sequence& arg_list) + { + switch (arg_list.size()) + { + case 0 : return T(0); + case 1 : return process_1(arg_list); + case 2 : return process_2(arg_list); + case 3 : return process_3(arg_list); + case 4 : return process_4(arg_list); + case 5 : return process_5(arg_list); + default : return vararg_add_op::process(arg_list) / T(arg_list.size()); + } + } + + template + static inline T process_1(const Sequence& arg_list) + { + return value(arg_list[0]); + } + + template + static inline T process_2(const Sequence& arg_list) + { + return (value(arg_list[0]) + value(arg_list[1])) / T(2); + } + + template + static inline T process_3(const Sequence& arg_list) + { + return (value(arg_list[0]) + value(arg_list[1]) + value(arg_list[2])) / T(3); + } + + template + static inline T process_4(const Sequence& arg_list) + { + return (value(arg_list[0]) + value(arg_list[1]) + + value(arg_list[2]) + value(arg_list[3])) / T(4); + } + + template + static inline T process_5(const Sequence& arg_list) + { + return (value(arg_list[0]) + value(arg_list[1]) + + value(arg_list[2]) + value(arg_list[3]) + + value(arg_list[4])) / T(5); + } + }; + + template + struct vararg_min_op exprtk_final : public opr_base + { + typedef typename opr_base::Type Type; + + template class Sequence> + static inline T process(const Sequence& arg_list) + { + switch (arg_list.size()) + { + case 0 : return T(0); + case 1 : return process_1(arg_list); + case 2 : return process_2(arg_list); + case 3 : return process_3(arg_list); + case 4 : return process_4(arg_list); + case 5 : return process_5(arg_list); + default : + { + T result = T(value(arg_list[0])); + + for (std::size_t i = 1; i < arg_list.size(); ++i) + { + const T v = value(arg_list[i]); + + if (v < result) + result = v; + } + + return result; + } + } + } + + template + static inline T process_1(const Sequence& arg_list) + { + return value(arg_list[0]); + } + + template + static inline T process_2(const Sequence& arg_list) + { + return std::min(value(arg_list[0]),value(arg_list[1])); + } + + template + static inline T process_3(const Sequence& arg_list) + { + return std::min(std::min(value(arg_list[0]),value(arg_list[1])),value(arg_list[2])); + } + + template + static inline T process_4(const Sequence& arg_list) + { + return std::min( + std::min(value(arg_list[0]), value(arg_list[1])), + std::min(value(arg_list[2]), value(arg_list[3]))); + } + + template + static inline T process_5(const Sequence& arg_list) + { + return std::min( + std::min(std::min(value(arg_list[0]), value(arg_list[1])), + std::min(value(arg_list[2]), value(arg_list[3]))), + value(arg_list[4])); + } + }; + + template + struct vararg_max_op exprtk_final : public opr_base + { + typedef typename opr_base::Type Type; + + template class Sequence> + static inline T process(const Sequence& arg_list) + { + switch (arg_list.size()) + { + case 0 : return T(0); + case 1 : return process_1(arg_list); + case 2 : return process_2(arg_list); + case 3 : return process_3(arg_list); + case 4 : return process_4(arg_list); + case 5 : return process_5(arg_list); + default : + { + T result = T(value(arg_list[0])); + + for (std::size_t i = 1; i < arg_list.size(); ++i) + { + const T v = value(arg_list[i]); + + if (v > result) + result = v; + } + + return result; + } + } + } + + template + static inline T process_1(const Sequence& arg_list) + { + return value(arg_list[0]); + } + + template + static inline T process_2(const Sequence& arg_list) + { + return std::max(value(arg_list[0]),value(arg_list[1])); + } + + template + static inline T process_3(const Sequence& arg_list) + { + return std::max(std::max(value(arg_list[0]),value(arg_list[1])),value(arg_list[2])); + } + + template + static inline T process_4(const Sequence& arg_list) + { + return std::max( + std::max(value(arg_list[0]), value(arg_list[1])), + std::max(value(arg_list[2]), value(arg_list[3]))); + } + + template + static inline T process_5(const Sequence& arg_list) + { + return std::max( + std::max(std::max(value(arg_list[0]), value(arg_list[1])), + std::max(value(arg_list[2]), value(arg_list[3]))), + value(arg_list[4])); + } + }; + + template + struct vararg_mand_op exprtk_final : public opr_base + { + typedef typename opr_base::Type Type; + + template class Sequence> + static inline T process(const Sequence& arg_list) + { + switch (arg_list.size()) + { + case 1 : return process_1(arg_list); + case 2 : return process_2(arg_list); + case 3 : return process_3(arg_list); + case 4 : return process_4(arg_list); + case 5 : return process_5(arg_list); + default : + { + for (std::size_t i = 0; i < arg_list.size(); ++i) + { + if (std::equal_to()(T(0), value(arg_list[i]))) + return T(0); + } + + return T(1); + } + } + } + + template + static inline T process_1(const Sequence& arg_list) + { + return std::not_equal_to() + (T(0), value(arg_list[0])) ? T(1) : T(0); + } + + template + static inline T process_2(const Sequence& arg_list) + { + return ( + std::not_equal_to()(T(0), value(arg_list[0])) && + std::not_equal_to()(T(0), value(arg_list[1])) + ) ? T(1) : T(0); + } + + template + static inline T process_3(const Sequence& arg_list) + { + return ( + std::not_equal_to()(T(0), value(arg_list[0])) && + std::not_equal_to()(T(0), value(arg_list[1])) && + std::not_equal_to()(T(0), value(arg_list[2])) + ) ? T(1) : T(0); + } + + template + static inline T process_4(const Sequence& arg_list) + { + return ( + std::not_equal_to()(T(0), value(arg_list[0])) && + std::not_equal_to()(T(0), value(arg_list[1])) && + std::not_equal_to()(T(0), value(arg_list[2])) && + std::not_equal_to()(T(0), value(arg_list[3])) + ) ? T(1) : T(0); + } + + template + static inline T process_5(const Sequence& arg_list) + { + return ( + std::not_equal_to()(T(0), value(arg_list[0])) && + std::not_equal_to()(T(0), value(arg_list[1])) && + std::not_equal_to()(T(0), value(arg_list[2])) && + std::not_equal_to()(T(0), value(arg_list[3])) && + std::not_equal_to()(T(0), value(arg_list[4])) + ) ? T(1) : T(0); + } + }; + + template + struct vararg_mor_op exprtk_final : public opr_base + { + typedef typename opr_base::Type Type; + + template class Sequence> + static inline T process(const Sequence& arg_list) + { + switch (arg_list.size()) + { + case 1 : return process_1(arg_list); + case 2 : return process_2(arg_list); + case 3 : return process_3(arg_list); + case 4 : return process_4(arg_list); + case 5 : return process_5(arg_list); + default : + { + for (std::size_t i = 0; i < arg_list.size(); ++i) + { + if (std::not_equal_to()(T(0), value(arg_list[i]))) + return T(1); + } + + return T(0); + } + } + } + + template + static inline T process_1(const Sequence& arg_list) + { + return std::not_equal_to() + (T(0), value(arg_list[0])) ? T(1) : T(0); + } + + template + static inline T process_2(const Sequence& arg_list) + { + return ( + std::not_equal_to()(T(0), value(arg_list[0])) || + std::not_equal_to()(T(0), value(arg_list[1])) + ) ? T(1) : T(0); + } + + template + static inline T process_3(const Sequence& arg_list) + { + return ( + std::not_equal_to()(T(0), value(arg_list[0])) || + std::not_equal_to()(T(0), value(arg_list[1])) || + std::not_equal_to()(T(0), value(arg_list[2])) + ) ? T(1) : T(0); + } + + template + static inline T process_4(const Sequence& arg_list) + { + return ( + std::not_equal_to()(T(0), value(arg_list[0])) || + std::not_equal_to()(T(0), value(arg_list[1])) || + std::not_equal_to()(T(0), value(arg_list[2])) || + std::not_equal_to()(T(0), value(arg_list[3])) + ) ? T(1) : T(0); + } + + template + static inline T process_5(const Sequence& arg_list) + { + return ( + std::not_equal_to()(T(0), value(arg_list[0])) || + std::not_equal_to()(T(0), value(arg_list[1])) || + std::not_equal_to()(T(0), value(arg_list[2])) || + std::not_equal_to()(T(0), value(arg_list[3])) || + std::not_equal_to()(T(0), value(arg_list[4])) + ) ? T(1) : T(0); + } + }; + + template + struct vararg_multi_op exprtk_final : public opr_base + { + typedef typename opr_base::Type Type; + + template class Sequence> + static inline T process(const Sequence& arg_list) + { + switch (arg_list.size()) + { + case 0 : return std::numeric_limits::quiet_NaN(); + case 1 : return process_1(arg_list); + case 2 : return process_2(arg_list); + case 3 : return process_3(arg_list); + case 4 : return process_4(arg_list); + case 5 : return process_5(arg_list); + case 6 : return process_6(arg_list); + case 7 : return process_7(arg_list); + case 8 : return process_8(arg_list); + default : + { + for (std::size_t i = 0; i < (arg_list.size() - 1); ++i) + { + value(arg_list[i]); + } + return value(arg_list.back()); + } + } + } + + template + static inline T process_1(const Sequence& arg_list) + { + return value(arg_list[0]); + } + + template + static inline T process_2(const Sequence& arg_list) + { + value(arg_list[0]); + return value(arg_list[1]); + } + + template + static inline T process_3(const Sequence& arg_list) + { + value(arg_list[0]); + value(arg_list[1]); + return value(arg_list[2]); + } + + template + static inline T process_4(const Sequence& arg_list) + { + value(arg_list[0]); + value(arg_list[1]); + value(arg_list[2]); + return value(arg_list[3]); + } + + template + static inline T process_5(const Sequence& arg_list) + { + value(arg_list[0]); + value(arg_list[1]); + value(arg_list[2]); + value(arg_list[3]); + return value(arg_list[4]); + } + + template + static inline T process_6(const Sequence& arg_list) + { + value(arg_list[0]); + value(arg_list[1]); + value(arg_list[2]); + value(arg_list[3]); + value(arg_list[4]); + return value(arg_list[5]); + } + + template + static inline T process_7(const Sequence& arg_list) + { + value(arg_list[0]); + value(arg_list[1]); + value(arg_list[2]); + value(arg_list[3]); + value(arg_list[4]); + value(arg_list[5]); + return value(arg_list[6]); + } + + template + static inline T process_8(const Sequence& arg_list) + { + value(arg_list[0]); + value(arg_list[1]); + value(arg_list[2]); + value(arg_list[3]); + value(arg_list[4]); + value(arg_list[5]); + value(arg_list[6]); + return value(arg_list[7]); + } + }; + + template + struct vec_add_op + { + typedef vector_interface* ivector_ptr; + + static inline T process(const ivector_ptr v) + { + const T* vec = v->vec()->vds().data(); + const std::size_t vec_size = v->size(); + + loop_unroll::details lud(vec_size); + + if (vec_size <= static_cast(lud.batch_size)) + { + T result = T(0); + int i = 0; + + switch (vec_size) + { + #define case_stmt(N,fall_through) \ + case N : result += vec[i++]; \ + fall_through \ + + #ifndef exprtk_disable_superscalar_unroll + case_stmt(16, exprtk_fallthrough) case_stmt(15, exprtk_fallthrough) + case_stmt(14, exprtk_fallthrough) case_stmt(13, exprtk_fallthrough) + case_stmt(12, exprtk_fallthrough) case_stmt(11, exprtk_fallthrough) + case_stmt(10, exprtk_fallthrough) case_stmt( 9, exprtk_fallthrough) + case_stmt( 8, exprtk_fallthrough) case_stmt( 7, exprtk_fallthrough) + case_stmt( 6, exprtk_fallthrough) case_stmt( 5, exprtk_fallthrough) + + #endif + case_stmt( 4, exprtk_fallthrough) case_stmt( 3, exprtk_fallthrough) + case_stmt( 2, exprtk_fallthrough) case_stmt( 1, (void)0;) + } + + #undef case_stmt + + return result; + } + + T r[] = { + T(0), T(0), T(0), T(0), T(0), T(0), T(0), T(0), + T(0), T(0), T(0), T(0), T(0), T(0), T(0), T(0) + }; + + const T* upper_bound = vec + lud.upper_bound; + + while (vec < upper_bound) + { + #define exprtk_loop(N) \ + r[N] += vec[N]; \ + + exprtk_loop( 0) exprtk_loop( 1) + exprtk_loop( 2) exprtk_loop( 3) + #ifndef exprtk_disable_superscalar_unroll + exprtk_loop( 4) exprtk_loop( 5) + exprtk_loop( 6) exprtk_loop( 7) + exprtk_loop( 8) exprtk_loop( 9) + exprtk_loop(10) exprtk_loop(11) + exprtk_loop(12) exprtk_loop(13) + exprtk_loop(14) exprtk_loop(15) + #endif + + vec += lud.batch_size; + } + + int i = 0; + + switch (lud.remainder) + { + #define case_stmt(N,fall_through) \ + case N : r[0] += vec[i++]; \ + fall_through \ + + #ifndef exprtk_disable_superscalar_unroll + case_stmt(15, exprtk_fallthrough) case_stmt(14, exprtk_fallthrough) + case_stmt(13, exprtk_fallthrough) case_stmt(12, exprtk_fallthrough) + case_stmt(11, exprtk_fallthrough) case_stmt(10, exprtk_fallthrough) + case_stmt( 9, exprtk_fallthrough) case_stmt( 8, exprtk_fallthrough) + case_stmt( 7, exprtk_fallthrough) case_stmt( 6, exprtk_fallthrough) + case_stmt( 5, exprtk_fallthrough) case_stmt( 4, exprtk_fallthrough) + #endif + case_stmt( 3, exprtk_fallthrough) case_stmt( 2, exprtk_fallthrough) + case_stmt( 1, (void)0;) + } + + #undef exprtk_loop + #undef case_stmt + + return (r[ 0] + r[ 1] + r[ 2] + r[ 3]) + #ifndef exprtk_disable_superscalar_unroll + + (r[ 4] + r[ 5] + r[ 6] + r[ 7]) + + (r[ 8] + r[ 9] + r[10] + r[11]) + + (r[12] + r[13] + r[14] + r[15]) + #endif + ; + } + }; + + template + struct vec_mul_op + { + typedef vector_interface* ivector_ptr; + + static inline T process(const ivector_ptr v) + { + const T* vec = v->vec()->vds().data(); + const std::size_t vec_size = v->vec()->size(); + + loop_unroll::details lud(vec_size); + + if (vec_size <= static_cast(lud.batch_size)) + { + T result = T(1); + int i = 0; + + switch (vec_size) + { + #define case_stmt(N,fall_through) \ + case N : result *= vec[i++]; \ + fall_through \ + + #ifndef exprtk_disable_superscalar_unroll + case_stmt(16, exprtk_fallthrough) case_stmt(15, exprtk_fallthrough) + case_stmt(14, exprtk_fallthrough) case_stmt(13, exprtk_fallthrough) + case_stmt(12, exprtk_fallthrough) case_stmt(11, exprtk_fallthrough) + case_stmt(10, exprtk_fallthrough) case_stmt( 9, exprtk_fallthrough) + case_stmt( 8, exprtk_fallthrough) case_stmt( 7, exprtk_fallthrough) + case_stmt( 6, exprtk_fallthrough) case_stmt( 5, exprtk_fallthrough) + #endif + case_stmt( 4, exprtk_fallthrough) case_stmt( 3, exprtk_fallthrough) + case_stmt( 2, exprtk_fallthrough) case_stmt( 1, (void)0;) + } + + #undef case_stmt + + return result; + } + + T r[] = { + T(1), T(1), T(1), T(1), T(1), T(1), T(1), T(1), + T(1), T(1), T(1), T(1), T(1), T(1), T(1), T(1) + }; + + const T* upper_bound = vec + lud.upper_bound; + + while (vec < upper_bound) + { + #define exprtk_loop(N) \ + r[N] *= vec[N]; \ + + exprtk_loop( 0) exprtk_loop( 1) + exprtk_loop( 2) exprtk_loop( 3) + #ifndef exprtk_disable_superscalar_unroll + exprtk_loop( 4) exprtk_loop( 5) + exprtk_loop( 6) exprtk_loop( 7) + exprtk_loop( 8) exprtk_loop( 9) + exprtk_loop(10) exprtk_loop(11) + exprtk_loop(12) exprtk_loop(13) + exprtk_loop(14) exprtk_loop(15) + #endif + + vec += lud.batch_size; + } + + int i = 0; + + switch (lud.remainder) + { + #define case_stmt(N,fall_through) \ + case N : r[0] *= vec[i++]; \ + fall_through \ + + #ifndef exprtk_disable_superscalar_unroll + case_stmt(15, exprtk_fallthrough) case_stmt(14, exprtk_fallthrough) + case_stmt(13, exprtk_fallthrough) case_stmt(12, exprtk_fallthrough) + case_stmt(11, exprtk_fallthrough) case_stmt(10, exprtk_fallthrough) + case_stmt( 9, exprtk_fallthrough) case_stmt( 8, exprtk_fallthrough) + case_stmt( 7, exprtk_fallthrough) case_stmt( 6, exprtk_fallthrough) + case_stmt( 5, exprtk_fallthrough) case_stmt( 4, exprtk_fallthrough) + #endif + case_stmt( 3, exprtk_fallthrough) case_stmt( 2, exprtk_fallthrough) + case_stmt( 1, (void)0;) + } + + #undef exprtk_loop + #undef case_stmt + + return (r[ 0] * r[ 1] * r[ 2] * r[ 3]) + #ifndef exprtk_disable_superscalar_unroll + * (r[ 4] * r[ 5] * r[ 6] * r[ 7]) + * (r[ 8] * r[ 9] * r[10] * r[11]) + * (r[12] * r[13] * r[14] * r[15]) + #endif + ; + } + }; + + template + struct vec_avg_op + { + typedef vector_interface* ivector_ptr; + + static inline T process(const ivector_ptr v) + { + const T vec_size = T(v->vec()->size()); + return vec_add_op::process(v) / vec_size; + } + }; + + template + struct vec_min_op + { + typedef vector_interface* ivector_ptr; + + static inline T process(const ivector_ptr v) + { + const T* vec = v->vec()->vds().data(); + const std::size_t vec_size = v->vec()->size(); + + T result = vec[0]; + + for (std::size_t i = 1; i < vec_size; ++i) + { + const T v_i = vec[i]; + + if (v_i < result) + result = v_i; + } + + return result; + } + }; + + template + struct vec_max_op + { + typedef vector_interface* ivector_ptr; + + static inline T process(const ivector_ptr v) + { + const T* vec = v->vec()->vds().data(); + const std::size_t vec_size = v->vec()->size(); + + T result = vec[0]; + + for (std::size_t i = 1; i < vec_size; ++i) + { + const T v_i = vec[i]; + + if (v_i > result) + result = v_i; + } + + return result; + } + }; + + template + class vov_base_node : public expression_node + { + public: + + virtual ~vov_base_node() + {} + + inline virtual operator_type operation() const + { + return details::e_default; + } + + virtual const T& v0() const = 0; + + virtual const T& v1() const = 0; + }; + + template + class cov_base_node : public expression_node + { + public: + + virtual ~cov_base_node() + {} + + inline virtual operator_type operation() const + { + return details::e_default; + } + + virtual const T c() const = 0; + + virtual const T& v() const = 0; + }; + + template + class voc_base_node : public expression_node + { + public: + + virtual ~voc_base_node() + {} + + inline virtual operator_type operation() const + { + return details::e_default; + } + + virtual const T c() const = 0; + + virtual const T& v() const = 0; + }; + + template + class vob_base_node : public expression_node + { + public: + + virtual ~vob_base_node() + {} + + virtual const T& v() const = 0; + }; + + template + class bov_base_node : public expression_node + { + public: + + virtual ~bov_base_node() + {} + + virtual const T& v() const = 0; + }; + + template + class cob_base_node : public expression_node + { + public: + + virtual ~cob_base_node() + {} + + inline virtual operator_type operation() const + { + return details::e_default; + } + + virtual const T c() const = 0; + + virtual void set_c(const T) = 0; + + virtual expression_node* move_branch(const std::size_t& index) = 0; + }; + + template + class boc_base_node : public expression_node + { + public: + + virtual ~boc_base_node() + {} + + inline virtual operator_type operation() const + { + return details::e_default; + } + + virtual const T c() const = 0; + + virtual void set_c(const T) = 0; + + virtual expression_node* move_branch(const std::size_t& index) = 0; + }; + + template + class uv_base_node : public expression_node + { + public: + + virtual ~uv_base_node() + {} + + inline virtual operator_type operation() const + { + return details::e_default; + } + + virtual const T& v() const = 0; + }; + + template + class sos_base_node : public expression_node + { + public: + + virtual ~sos_base_node() + {} + + inline virtual operator_type operation() const + { + return details::e_default; + } + }; + + template + class sosos_base_node : public expression_node + { + public: + + virtual ~sosos_base_node() + {} + + inline virtual operator_type operation() const + { + return details::e_default; + } + }; + + template + class T0oT1oT2_base_node : public expression_node + { + public: + + virtual ~T0oT1oT2_base_node() + {} + + virtual std::string type_id() const = 0; + }; + + template + class T0oT1oT2oT3_base_node : public expression_node + { + public: + + virtual ~T0oT1oT2oT3_base_node() + {} + + virtual std::string type_id() const = 0; + }; + + template + class unary_variable_node exprtk_final : public uv_base_node + { + public: + + typedef expression_node* expression_ptr; + typedef Operation operation_t; + + explicit unary_variable_node(const T& var) + : v_(var) + {} + + inline T value() const exprtk_override + { + return Operation::process(v_); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return Operation::type(); + } + + inline operator_type operation() const exprtk_override + { + return Operation::operation(); + } + + inline const T& v() const exprtk_override + { + return v_; + } + + private: + + unary_variable_node(const unary_variable_node&) exprtk_delete; + unary_variable_node& operator=(const unary_variable_node&) exprtk_delete; + + const T& v_; + }; + + template + class uvouv_node exprtk_final : public expression_node + { + public: + + // UOpr1(v0) Op UOpr2(v1) + typedef typename details::functor_t functor_t; + typedef typename functor_t::bfunc_t bfunc_t; + typedef typename functor_t::ufunc_t ufunc_t; + typedef expression_node* expression_ptr; + + explicit uvouv_node(const T& var0,const T& var1, + ufunc_t uf0, ufunc_t uf1, bfunc_t bf) + : v0_(var0) + , v1_(var1) + , u0_(uf0 ) + , u1_(uf1 ) + , f_ (bf ) + {} + + inline T value() const exprtk_override + { + return f_(u0_(v0_),u1_(v1_)); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_uvouv; + } + + inline const T& v0() + { + return v0_; + } + + inline const T& v1() + { + return v1_; + } + + inline ufunc_t u0() + { + return u0_; + } + + inline ufunc_t u1() + { + return u1_; + } + + inline ufunc_t f() + { + return f_; + } + + private: + + uvouv_node(const uvouv_node&) exprtk_delete; + uvouv_node& operator=(const uvouv_node&) exprtk_delete; + + const T& v0_; + const T& v1_; + const ufunc_t u0_; + const ufunc_t u1_; + const bfunc_t f_; + }; + + template + class unary_branch_node exprtk_final : public expression_node + { + public: + + typedef Operation operation_t; + typedef expression_node* expression_ptr; + typedef std::pair branch_t; + + explicit unary_branch_node(expression_ptr branch) + { + construct_branch_pair(branch_, branch); + } + + inline T value() const exprtk_override + { + return Operation::process(branch_.first->value()); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return Operation::type(); + } + + inline bool valid() const exprtk_override + { + return branch_.first && branch_.first->valid(); + } + + inline operator_type operation() + { + return Operation::operation(); + } + + inline expression_node* branch(const std::size_t&) const exprtk_override + { + return branch_.first; + } + + inline void release() + { + branch_.second = false; + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(branch_, node_delete_list); + } + + std::size_t node_depth() const exprtk_override + { + return expression_node::ndb_t::compute_node_depth(branch_); + } + + private: + + unary_branch_node(const unary_branch_node&) exprtk_delete; + unary_branch_node& operator=(const unary_branch_node&) exprtk_delete; + + branch_t branch_; + }; + + template struct is_const { enum {result = 0}; }; + template struct is_const { enum {result = 1}; }; + template struct is_const_ref { enum {result = 0}; }; + template struct is_const_ref { enum {result = 1}; }; + template struct is_ref { enum {result = 0}; }; + template struct is_ref { enum {result = 1}; }; + template struct is_ref { enum {result = 0}; }; + + template + struct param_to_str { static std::string result() { static const std::string r("v"); return r; } }; + + template <> + struct param_to_str<0> { static std::string result() { static const std::string r("c"); return r; } }; + + #define exprtk_crtype(Type) \ + param_to_str::result>::result() \ + + template + struct T0oT1oT2process + { + typedef typename details::functor_t functor_t; + typedef typename functor_t::bfunc_t bfunc_t; + + struct mode0 + { + static inline T process(const T& t0, const T& t1, const T& t2, const bfunc_t bf0, const bfunc_t bf1) + { + // (T0 o0 T1) o1 T2 + return bf1(bf0(t0,t1),t2); + } + + template + static inline std::string id() + { + static const std::string result = "(" + exprtk_crtype(T0) + "o" + + exprtk_crtype(T1) + ")o(" + + exprtk_crtype(T2) + ")" ; + return result; + } + }; + + struct mode1 + { + static inline T process(const T& t0, const T& t1, const T& t2, const bfunc_t bf0, const bfunc_t bf1) + { + // T0 o0 (T1 o1 T2) + return bf0(t0,bf1(t1,t2)); + } + + template + static inline std::string id() + { + static const std::string result = "(" + exprtk_crtype(T0) + ")o(" + + exprtk_crtype(T1) + "o" + + exprtk_crtype(T2) + ")" ; + return result; + } + }; + }; + + template + struct T0oT1oT20T3process + { + typedef typename details::functor_t functor_t; + typedef typename functor_t::bfunc_t bfunc_t; + + struct mode0 + { + static inline T process(const T& t0, const T& t1, + const T& t2, const T& t3, + const bfunc_t bf0, const bfunc_t bf1, const bfunc_t bf2) + { + // (T0 o0 T1) o1 (T2 o2 T3) + return bf1(bf0(t0,t1),bf2(t2,t3)); + } + + template + static inline std::string id() + { + static const std::string result = "(" + exprtk_crtype(T0) + "o" + + exprtk_crtype(T1) + ")o" + + "(" + exprtk_crtype(T2) + "o" + + exprtk_crtype(T3) + ")" ; + return result; + } + }; + + struct mode1 + { + static inline T process(const T& t0, const T& t1, + const T& t2, const T& t3, + const bfunc_t bf0, const bfunc_t bf1, const bfunc_t bf2) + { + // (T0 o0 (T1 o1 (T2 o2 T3)) + return bf0(t0,bf1(t1,bf2(t2,t3))); + } + template + static inline std::string id() + { + static const std::string result = "(" + exprtk_crtype(T0) + ")o((" + + exprtk_crtype(T1) + ")o(" + + exprtk_crtype(T2) + "o" + + exprtk_crtype(T3) + "))" ; + return result; + } + }; + + struct mode2 + { + static inline T process(const T& t0, const T& t1, + const T& t2, const T& t3, + const bfunc_t bf0, const bfunc_t bf1, const bfunc_t bf2) + { + // (T0 o0 ((T1 o1 T2) o2 T3) + return bf0(t0,bf2(bf1(t1,t2),t3)); + } + + template + static inline std::string id() + { + static const std::string result = "(" + exprtk_crtype(T0) + ")o((" + + exprtk_crtype(T1) + "o" + + exprtk_crtype(T2) + ")o(" + + exprtk_crtype(T3) + "))" ; + return result; + } + }; + + struct mode3 + { + static inline T process(const T& t0, const T& t1, + const T& t2, const T& t3, + const bfunc_t bf0, const bfunc_t bf1, const bfunc_t bf2) + { + // (((T0 o0 T1) o1 T2) o2 T3) + return bf2(bf1(bf0(t0,t1),t2),t3); + } + + template + static inline std::string id() + { + static const std::string result = "((" + exprtk_crtype(T0) + "o" + + exprtk_crtype(T1) + ")o(" + + exprtk_crtype(T2) + "))o(" + + exprtk_crtype(T3) + ")"; + return result; + } + }; + + struct mode4 + { + static inline T process(const T& t0, const T& t1, + const T& t2, const T& t3, + const bfunc_t bf0, const bfunc_t bf1, const bfunc_t bf2) + { + // ((T0 o0 (T1 o1 T2)) o2 T3 + return bf2(bf0(t0,bf1(t1,t2)),t3); + } + + template + static inline std::string id() + { + static const std::string result = "((" + exprtk_crtype(T0) + ")o(" + + exprtk_crtype(T1) + "o" + + exprtk_crtype(T2) + "))o(" + + exprtk_crtype(T3) + ")" ; + return result; + } + }; + }; + + #undef exprtk_crtype + + template + struct nodetype_T0oT1 { static const typename expression_node::node_type result; }; + template + const typename expression_node::node_type nodetype_T0oT1::result = expression_node::e_none; + + #define synthesis_node_type_define(T0_, T1_, v_) \ + template \ + struct nodetype_T0oT1 { static const typename expression_node::node_type result; }; \ + template \ + const typename expression_node::node_type nodetype_T0oT1::result = expression_node:: v_; \ + + synthesis_node_type_define(const T0&, const T1&, e_vov) + synthesis_node_type_define(const T0&, const T1 , e_voc) + synthesis_node_type_define(const T0 , const T1&, e_cov) + synthesis_node_type_define( T0&, T1&, e_none) + synthesis_node_type_define(const T0 , const T1 , e_none) + synthesis_node_type_define( T0&, const T1 , e_none) + synthesis_node_type_define(const T0 , T1&, e_none) + synthesis_node_type_define(const T0&, T1&, e_none) + synthesis_node_type_define( T0&, const T1&, e_none) + #undef synthesis_node_type_define + + template + struct nodetype_T0oT1oT2 { static const typename expression_node::node_type result; }; + template + const typename expression_node::node_type nodetype_T0oT1oT2::result = expression_node::e_none; + + #define synthesis_node_type_define(T0_, T1_, T2_, v_) \ + template \ + struct nodetype_T0oT1oT2 { static const typename expression_node::node_type result; }; \ + template \ + const typename expression_node::node_type nodetype_T0oT1oT2::result = expression_node:: v_; \ + + synthesis_node_type_define(const T0&, const T1&, const T2&, e_vovov) + synthesis_node_type_define(const T0&, const T1&, const T2 , e_vovoc) + synthesis_node_type_define(const T0&, const T1 , const T2&, e_vocov) + synthesis_node_type_define(const T0 , const T1&, const T2&, e_covov) + synthesis_node_type_define(const T0 , const T1&, const T2 , e_covoc) + synthesis_node_type_define(const T0 , const T1 , const T2 , e_none ) + synthesis_node_type_define(const T0 , const T1 , const T2&, e_none ) + synthesis_node_type_define(const T0&, const T1 , const T2 , e_none ) + synthesis_node_type_define( T0&, T1&, T2&, e_none ) + #undef synthesis_node_type_define + + template + struct nodetype_T0oT1oT2oT3 { static const typename expression_node::node_type result; }; + template + const typename expression_node::node_type nodetype_T0oT1oT2oT3::result = expression_node::e_none; + + #define synthesis_node_type_define(T0_, T1_, T2_, T3_, v_) \ + template \ + struct nodetype_T0oT1oT2oT3 { static const typename expression_node::node_type result; }; \ + template \ + const typename expression_node::node_type nodetype_T0oT1oT2oT3::result = expression_node:: v_; \ + + synthesis_node_type_define(const T0&, const T1&, const T2&, const T3&, e_vovovov) + synthesis_node_type_define(const T0&, const T1&, const T2&, const T3 , e_vovovoc) + synthesis_node_type_define(const T0&, const T1&, const T2 , const T3&, e_vovocov) + synthesis_node_type_define(const T0&, const T1 , const T2&, const T3&, e_vocovov) + synthesis_node_type_define(const T0 , const T1&, const T2&, const T3&, e_covovov) + synthesis_node_type_define(const T0 , const T1&, const T2 , const T3&, e_covocov) + synthesis_node_type_define(const T0&, const T1 , const T2&, const T3 , e_vocovoc) + synthesis_node_type_define(const T0 , const T1&, const T2&, const T3 , e_covovoc) + synthesis_node_type_define(const T0&, const T1 , const T2 , const T3&, e_vococov) + synthesis_node_type_define(const T0 , const T1 , const T2 , const T3 , e_none ) + synthesis_node_type_define(const T0 , const T1 , const T2 , const T3&, e_none ) + synthesis_node_type_define(const T0 , const T1 , const T2&, const T3 , e_none ) + synthesis_node_type_define(const T0 , const T1&, const T2 , const T3 , e_none ) + synthesis_node_type_define(const T0&, const T1 , const T2 , const T3 , e_none ) + synthesis_node_type_define(const T0 , const T1 , const T2&, const T3&, e_none ) + synthesis_node_type_define(const T0&, const T1&, const T2 , const T3 , e_none ) + #undef synthesis_node_type_define + + template + class T0oT1 exprtk_final : public expression_node + { + public: + + typedef typename details::functor_t functor_t; + typedef typename functor_t::bfunc_t bfunc_t; + typedef T value_type; + typedef T0oT1 node_type; + + T0oT1(T0 p0, T1 p1, const bfunc_t p2) + : t0_(p0) + , t1_(p1) + , f_ (p2) + {} + + inline typename expression_node::node_type type() const exprtk_override + { + static const typename expression_node::node_type result = nodetype_T0oT1::result; + return result; + } + + inline operator_type operation() const exprtk_override + { + return e_default; + } + + inline T value() const exprtk_override + { + return f_(t0_,t1_); + } + + inline T0 t0() const + { + return t0_; + } + + inline T1 t1() const + { + return t1_; + } + + inline bfunc_t f() const + { + return f_; + } + + template + static inline expression_node* allocate(Allocator& allocator, + T0 p0, T1 p1, + bfunc_t p2) + { + return allocator + .template allocate_type + (p0, p1, p2); + } + + private: + + T0oT1(const T0oT1&) exprtk_delete; + T0oT1& operator=(const T0oT1&) { return (*this); } + + T0 t0_; + T1 t1_; + const bfunc_t f_; + }; + + template + class T0oT1oT2 exprtk_final : public T0oT1oT2_base_node + { + public: + + typedef typename details::functor_t functor_t; + typedef typename functor_t::bfunc_t bfunc_t; + typedef T value_type; + typedef T0oT1oT2 node_type; + typedef ProcessMode process_mode_t; + + T0oT1oT2(T0 p0, T1 p1, T2 p2, const bfunc_t p3, const bfunc_t p4) + : t0_(p0) + , t1_(p1) + , t2_(p2) + , f0_(p3) + , f1_(p4) + {} + + inline typename expression_node::node_type type() const exprtk_override + { + static const typename expression_node::node_type result = nodetype_T0oT1oT2::result; + return result; + } + + inline operator_type operation() + { + return e_default; + } + + inline T value() const exprtk_override + { + return ProcessMode::process(t0_, t1_, t2_, f0_, f1_); + } + + inline T0 t0() const + { + return t0_; + } + + inline T1 t1() const + { + return t1_; + } + + inline T2 t2() const + { + return t2_; + } + + bfunc_t f0() const + { + return f0_; + } + + bfunc_t f1() const + { + return f1_; + } + + std::string type_id() const exprtk_override + { + return id(); + } + + static inline std::string id() + { + return process_mode_t::template id(); + } + + template + static inline expression_node* allocate(Allocator& allocator, T0 p0, T1 p1, T2 p2, bfunc_t p3, bfunc_t p4) + { + return allocator + .template allocate_type + (p0, p1, p2, p3, p4); + } + + private: + + T0oT1oT2(const node_type&) exprtk_delete; + node_type& operator=(const node_type&) exprtk_delete; + + T0 t0_; + T1 t1_; + T2 t2_; + const bfunc_t f0_; + const bfunc_t f1_; + }; + + template + class T0oT1oT2oT3 exprtk_final : public T0oT1oT2oT3_base_node + { + public: + + typedef typename details::functor_t functor_t; + typedef typename functor_t::bfunc_t bfunc_t; + typedef T value_type; + typedef T0_ T0; + typedef T1_ T1; + typedef T2_ T2; + typedef T3_ T3; + typedef T0oT1oT2oT3 node_type; + typedef ProcessMode process_mode_t; + + T0oT1oT2oT3(T0 p0, T1 p1, T2 p2, T3 p3, bfunc_t p4, bfunc_t p5, bfunc_t p6) + : t0_(p0) + , t1_(p1) + , t2_(p2) + , t3_(p3) + , f0_(p4) + , f1_(p5) + , f2_(p6) + {} + + inline T value() const exprtk_override + { + return ProcessMode::process(t0_, t1_, t2_, t3_, f0_, f1_, f2_); + } + + inline T0 t0() const + { + return t0_; + } + + inline T1 t1() const + { + return t1_; + } + + inline T2 t2() const + { + return t2_; + } + + inline T3 t3() const + { + return t3_; + } + + inline bfunc_t f0() const + { + return f0_; + } + + inline bfunc_t f1() const + { + return f1_; + } + + inline bfunc_t f2() const + { + return f2_; + } + + inline std::string type_id() const exprtk_override + { + return id(); + } + + static inline std::string id() + { + return process_mode_t::template id(); + } + + template + static inline expression_node* allocate(Allocator& allocator, + T0 p0, T1 p1, T2 p2, T3 p3, + bfunc_t p4, bfunc_t p5, bfunc_t p6) + { + return allocator + .template allocate_type + (p0, p1, p2, p3, p4, p5, p6); + } + + private: + + T0oT1oT2oT3(const node_type&) exprtk_delete; + node_type& operator=(const node_type&) exprtk_delete; + + T0 t0_; + T1 t1_; + T2 t2_; + T3 t3_; + const bfunc_t f0_; + const bfunc_t f1_; + const bfunc_t f2_; + }; + + template + class T0oT1oT2_sf3 exprtk_final : public T0oT1oT2_base_node + { + public: + + typedef typename details::functor_t functor_t; + typedef typename functor_t::tfunc_t tfunc_t; + typedef T value_type; + typedef T0oT1oT2_sf3 node_type; + + T0oT1oT2_sf3(T0 p0, T1 p1, T2 p2, const tfunc_t p3) + : t0_(p0) + , t1_(p1) + , t2_(p2) + , f_ (p3) + {} + + inline typename expression_node::node_type type() const exprtk_override + { + static const typename expression_node::node_type result = nodetype_T0oT1oT2::result; + return result; + } + + inline operator_type operation() const exprtk_override + { + return e_default; + } + + inline T value() const exprtk_override + { + return f_(t0_, t1_, t2_); + } + + inline T0 t0() const + { + return t0_; + } + + inline T1 t1() const + { + return t1_; + } + + inline T2 t2() const + { + return t2_; + } + + tfunc_t f() const + { + return f_; + } + + std::string type_id() const + { + return id(); + } + + static inline std::string id() + { + return "sf3"; + } + + template + static inline expression_node* allocate(Allocator& allocator, T0 p0, T1 p1, T2 p2, tfunc_t p3) + { + return allocator + .template allocate_type + (p0, p1, p2, p3); + } + + private: + + T0oT1oT2_sf3(const node_type&) exprtk_delete; + node_type& operator=(const node_type&) exprtk_delete; + + T0 t0_; + T1 t1_; + T2 t2_; + const tfunc_t f_; + }; + + template + class sf3ext_type_node : public T0oT1oT2_base_node + { + public: + + virtual ~sf3ext_type_node() + {} + + virtual T0 t0() const = 0; + + virtual T1 t1() const = 0; + + virtual T2 t2() const = 0; + }; + + template + class T0oT1oT2_sf3ext exprtk_final : public sf3ext_type_node + { + public: + + typedef T value_type; + typedef T0oT1oT2_sf3ext node_type; + + T0oT1oT2_sf3ext(T0 p0, T1 p1, T2 p2) + : t0_(p0) + , t1_(p1) + , t2_(p2) + {} + + inline typename expression_node::node_type type() const exprtk_override + { + static const typename expression_node::node_type result = nodetype_T0oT1oT2::result; + return result; + } + + inline operator_type operation() + { + return e_default; + } + + inline T value() const exprtk_override + { + return SF3Operation::process(t0_, t1_, t2_); + } + + T0 t0() const exprtk_override + { + return t0_; + } + + T1 t1() const exprtk_override + { + return t1_; + } + + T2 t2() const exprtk_override + { + return t2_; + } + + std::string type_id() const exprtk_override + { + return id(); + } + + static inline std::string id() + { + return SF3Operation::id(); + } + + template + static inline expression_node* allocate(Allocator& allocator, T0 p0, T1 p1, T2 p2) + { + return allocator + .template allocate_type + (p0, p1, p2); + } + + private: + + T0oT1oT2_sf3ext(const node_type&) exprtk_delete; + node_type& operator=(const node_type&) exprtk_delete; + + T0 t0_; + T1 t1_; + T2 t2_; + }; + + template + inline bool is_sf3ext_node(const expression_node* n) + { + switch (n->type()) + { + case expression_node::e_vovov : return true; + case expression_node::e_vovoc : return true; + case expression_node::e_vocov : return true; + case expression_node::e_covov : return true; + case expression_node::e_covoc : return true; + default : return false; + } + } + + template + class T0oT1oT2oT3_sf4 exprtk_final : public T0oT1oT2_base_node + { + public: + + typedef typename details::functor_t functor_t; + typedef typename functor_t::qfunc_t qfunc_t; + typedef T value_type; + typedef T0oT1oT2oT3_sf4 node_type; + + T0oT1oT2oT3_sf4(T0 p0, T1 p1, T2 p2, T3 p3, const qfunc_t p4) + : t0_(p0) + , t1_(p1) + , t2_(p2) + , t3_(p3) + , f_ (p4) + {} + + inline typename expression_node::node_type type() const exprtk_override + { + static const typename expression_node::node_type result = nodetype_T0oT1oT2oT3::result; + return result; + } + + inline operator_type operation() const exprtk_override + { + return e_default; + } + + inline T value() const exprtk_override + { + return f_(t0_, t1_, t2_, t3_); + } + + inline T0 t0() const + { + return t0_; + } + + inline T1 t1() const + { + return t1_; + } + + inline T2 t2() const + { + return t2_; + } + + inline T3 t3() const + { + return t3_; + } + + qfunc_t f() const + { + return f_; + } + + std::string type_id() const + { + return id(); + } + + static inline std::string id() + { + return "sf4"; + } + + template + static inline expression_node* allocate(Allocator& allocator, T0 p0, T1 p1, T2 p2, T3 p3, qfunc_t p4) + { + return allocator + .template allocate_type + (p0, p1, p2, p3, p4); + } + + private: + + T0oT1oT2oT3_sf4(const node_type&) exprtk_delete; + node_type& operator=(const node_type&) exprtk_delete; + + T0 t0_; + T1 t1_; + T2 t2_; + T3 t3_; + const qfunc_t f_; + }; + + template + class T0oT1oT2oT3_sf4ext exprtk_final : public T0oT1oT2oT3_base_node + { + public: + + typedef T value_type; + typedef T0oT1oT2oT3_sf4ext node_type; + + T0oT1oT2oT3_sf4ext(T0 p0, T1 p1, T2 p2, T3 p3) + : t0_(p0) + , t1_(p1) + , t2_(p2) + , t3_(p3) + {} + + inline typename expression_node::node_type type() const exprtk_override + { + static const typename expression_node::node_type result = nodetype_T0oT1oT2oT3::result; + return result; + } + + inline T value() const exprtk_override + { + return SF4Operation::process(t0_, t1_, t2_, t3_); + } + + inline T0 t0() const + { + return t0_; + } + + inline T1 t1() const + { + return t1_; + } + + inline T2 t2() const + { + return t2_; + } + + inline T3 t3() const + { + return t3_; + } + + std::string type_id() const exprtk_override + { + return id(); + } + + static inline std::string id() + { + return SF4Operation::id(); + } + + template + static inline expression_node* allocate(Allocator& allocator, T0 p0, T1 p1, T2 p2, T3 p3) + { + return allocator + .template allocate_type + (p0, p1, p2, p3); + } + + private: + + T0oT1oT2oT3_sf4ext(const node_type&) exprtk_delete; + node_type& operator=(const node_type&) exprtk_delete; + + T0 t0_; + T1 t1_; + T2 t2_; + T3 t3_; + }; + + template + inline bool is_sf4ext_node(const expression_node* n) + { + switch (n->type()) + { + case expression_node::e_vovovov : return true; + case expression_node::e_vovovoc : return true; + case expression_node::e_vovocov : return true; + case expression_node::e_vocovov : return true; + case expression_node::e_covovov : return true; + case expression_node::e_covocov : return true; + case expression_node::e_vocovoc : return true; + case expression_node::e_covovoc : return true; + case expression_node::e_vococov : return true; + default : return false; + } + } + + template + struct T0oT1_define + { + typedef details::T0oT1 type0; + }; + + template + struct T0oT1oT2_define + { + typedef details::T0oT1oT2::mode0> type0; + typedef details::T0oT1oT2::mode1> type1; + typedef details::T0oT1oT2_sf3 sf3_type; + typedef details::sf3ext_type_node sf3_type_node; + }; + + template + struct T0oT1oT2oT3_define + { + typedef details::T0oT1oT2oT3::mode0> type0; + typedef details::T0oT1oT2oT3::mode1> type1; + typedef details::T0oT1oT2oT3::mode2> type2; + typedef details::T0oT1oT2oT3::mode3> type3; + typedef details::T0oT1oT2oT3::mode4> type4; + typedef details::T0oT1oT2oT3_sf4 sf4_type; + }; + + template + class vov_node exprtk_final : public vov_base_node + { + public: + + typedef expression_node* expression_ptr; + typedef Operation operation_t; + + // variable op variable node + explicit vov_node(const T& var0, const T& var1) + : v0_(var0) + , v1_(var1) + {} + + inline T value() const exprtk_override + { + return Operation::process(v0_,v1_); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return Operation::type(); + } + + inline operator_type operation() const exprtk_override + { + return Operation::operation(); + } + + inline const T& v0() const exprtk_override + { + return v0_; + } + + inline const T& v1() const exprtk_override + { + return v1_; + } + + protected: + + const T& v0_; + const T& v1_; + + private: + + vov_node(const vov_node&) exprtk_delete; + vov_node& operator=(const vov_node&) exprtk_delete; + }; + + template + class cov_node exprtk_final : public cov_base_node + { + public: + + typedef expression_node* expression_ptr; + typedef Operation operation_t; + + // constant op variable node + explicit cov_node(const T& const_var, const T& var) + : c_(const_var) + , v_(var) + {} + + inline T value() const exprtk_override + { + return Operation::process(c_,v_); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return Operation::type(); + } + + inline operator_type operation() const exprtk_override + { + return Operation::operation(); + } + + inline const T c() const exprtk_override + { + return c_; + } + + inline const T& v() const exprtk_override + { + return v_; + } + + protected: + + const T c_; + const T& v_; + + private: + + cov_node(const cov_node&) exprtk_delete; + cov_node& operator=(const cov_node&) exprtk_delete; + }; + + template + class voc_node exprtk_final : public voc_base_node + { + public: + + typedef expression_node* expression_ptr; + typedef Operation operation_t; + + // variable op constant node + explicit voc_node(const T& var, const T& const_var) + : v_(var) + , c_(const_var) + {} + + inline T value() const exprtk_override + { + return Operation::process(v_,c_); + } + + inline operator_type operation() const exprtk_override + { + return Operation::operation(); + } + + inline const T c() const exprtk_override + { + return c_; + } + + inline const T& v() const exprtk_override + { + return v_; + } + + protected: + + const T& v_; + const T c_; + + private: + + voc_node(const voc_node&) exprtk_delete; + voc_node& operator=(const voc_node&) exprtk_delete; + }; + + template + class vob_node exprtk_final : public vob_base_node + { + public: + + typedef expression_node* expression_ptr; + typedef std::pair branch_t; + typedef Operation operation_t; + + // variable op binary node + explicit vob_node(const T& var, const expression_ptr branch) + : v_(var) + { + construct_branch_pair(branch_, branch); + assert(valid()); + } + + inline T value() const exprtk_override + { + return Operation::process(v_,branch_.first->value()); + } + + inline const T& v() const exprtk_override + { + return v_; + } + + inline bool valid() const exprtk_override + { + return branch_.first && branch_.first->valid(); + } + + inline expression_node* branch(const std::size_t&) const exprtk_override + { + return branch_.first; + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(branch_, node_delete_list); + } + + std::size_t node_depth() const exprtk_override + { + return expression_node::ndb_t::compute_node_depth(branch_); + } + + private: + + vob_node(const vob_node&) exprtk_delete; + vob_node& operator=(const vob_node&) exprtk_delete; + + const T& v_; + branch_t branch_; + }; + + template + class bov_node exprtk_final : public bov_base_node + { + public: + + typedef expression_node* expression_ptr; + typedef std::pair branch_t; + typedef Operation operation_t; + + // binary node op variable node + explicit bov_node(const expression_ptr branch, const T& var) + : v_(var) + { + construct_branch_pair(branch_, branch); + assert(valid()); + } + + inline T value() const exprtk_override + { + return Operation::process(branch_.first->value(),v_); + } + + inline const T& v() const exprtk_override + { + return v_; + } + + inline bool valid() const exprtk_override + { + return branch_.first && branch_.first->valid(); + } + + inline expression_node* branch(const std::size_t&) const exprtk_override + { + return branch_.first; + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(branch_, node_delete_list); + } + + std::size_t node_depth() const exprtk_override + { + return expression_node::ndb_t::compute_node_depth(branch_); + } + + private: + + bov_node(const bov_node&) exprtk_delete; + bov_node& operator=(const bov_node&) exprtk_delete; + + const T& v_; + branch_t branch_; + }; + + template + class cob_node exprtk_final : public cob_base_node + { + public: + + typedef expression_node* expression_ptr; + typedef std::pair branch_t; + typedef Operation operation_t; + + // constant op variable node + explicit cob_node(const T const_var, const expression_ptr branch) + : c_(const_var) + { + construct_branch_pair(branch_, branch); + assert(valid()); + } + + inline T value() const exprtk_override + { + return Operation::process(c_,branch_.first->value()); + } + + inline operator_type operation() const exprtk_override + { + return Operation::operation(); + } + + inline const T c() const exprtk_override + { + return c_; + } + + inline void set_c(const T new_c) exprtk_override + { + (*const_cast(&c_)) = new_c; + } + + inline bool valid() const exprtk_override + { + return branch_.first && branch_.first->valid(); + } + + inline expression_node* branch(const std::size_t&) const exprtk_override + { + return branch_.first; + } + + inline expression_node* move_branch(const std::size_t&) exprtk_override + { + branch_.second = false; + return branch_.first; + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(branch_, node_delete_list); + } + + std::size_t node_depth() const exprtk_override + { + return expression_node::ndb_t::compute_node_depth(branch_); + } + + private: + + cob_node(const cob_node&) exprtk_delete; + cob_node& operator=(const cob_node&) exprtk_delete; + + const T c_; + branch_t branch_; + }; + + template + class boc_node exprtk_final : public boc_base_node + { + public: + + typedef expression_node* expression_ptr; + typedef std::pair branch_t; + typedef Operation operation_t; + + // binary node op constant node + explicit boc_node(const expression_ptr branch, const T const_var) + : c_(const_var) + { + construct_branch_pair(branch_, branch); + assert(valid()); + } + + inline T value() const exprtk_override + { + return Operation::process(branch_.first->value(),c_); + } + + inline operator_type operation() const exprtk_override + { + return Operation::operation(); + } + + inline const T c() const exprtk_override + { + return c_; + } + + inline void set_c(const T new_c) exprtk_override + { + (*const_cast(&c_)) = new_c; + } + + inline bool valid() const exprtk_override + { + return branch_.first && branch_.first->valid(); + } + + inline expression_node* branch(const std::size_t&) const exprtk_override + { + return branch_.first; + } + + inline expression_node* move_branch(const std::size_t&) exprtk_override + { + branch_.second = false; + return branch_.first; + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(branch_, node_delete_list); + } + + std::size_t node_depth() const exprtk_override + { + return expression_node::ndb_t::compute_node_depth(branch_); + } + + private: + + boc_node(const boc_node&) exprtk_delete; + boc_node& operator=(const boc_node&) exprtk_delete; + + const T c_; + branch_t branch_; + }; + + #ifndef exprtk_disable_string_capabilities + template + class sos_node exprtk_final : public sos_base_node + { + public: + + typedef expression_node* expression_ptr; + typedef Operation operation_t; + + // string op string node + explicit sos_node(SType0 p0, SType1 p1) + : s0_(p0) + , s1_(p1) + {} + + inline T value() const exprtk_override + { + return Operation::process(s0_,s1_); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return Operation::type(); + } + + inline operator_type operation() const exprtk_override + { + return Operation::operation(); + } + + inline std::string& s0() + { + return s0_; + } + + inline std::string& s1() + { + return s1_; + } + + protected: + + SType0 s0_; + SType1 s1_; + + private: + + sos_node(const sos_node&) exprtk_delete; + sos_node& operator=(const sos_node&) exprtk_delete; + }; + + template + class str_xrox_node exprtk_final : public sos_base_node + { + public: + + typedef expression_node* expression_ptr; + typedef Operation operation_t; + typedef str_xrox_node node_type; + + // string-range op string node + explicit str_xrox_node(SType0 p0, SType1 p1, RangePack rp0) + : s0_ (p0 ) + , s1_ (p1 ) + , rp0_(rp0) + {} + + ~str_xrox_node() exprtk_override + { + rp0_.free(); + } + + inline T value() const exprtk_override + { + std::size_t r0 = 0; + std::size_t r1 = 0; + + if (rp0_(r0, r1, s0_.size())) + return Operation::process(s0_.substr(r0, (r1 - r0) + 1), s1_); + else + return T(0); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return Operation::type(); + } + + inline operator_type operation() const exprtk_override + { + return Operation::operation(); + } + + inline std::string& s0() + { + return s0_; + } + + inline std::string& s1() + { + return s1_; + } + + protected: + + SType0 s0_; + SType1 s1_; + RangePack rp0_; + + private: + + str_xrox_node(const node_type&) exprtk_delete; + node_type& operator=(const node_type&) exprtk_delete; + }; + + template + class str_xoxr_node exprtk_final : public sos_base_node + { + public: + + typedef expression_node* expression_ptr; + typedef Operation operation_t; + typedef str_xoxr_node node_type; + + // string op string range node + explicit str_xoxr_node(SType0 p0, SType1 p1, RangePack rp1) + : s0_ (p0 ) + , s1_ (p1 ) + , rp1_(rp1) + {} + + ~str_xoxr_node() + { + rp1_.free(); + } + + inline T value() const exprtk_override + { + std::size_t r0 = 0; + std::size_t r1 = 0; + + if (rp1_(r0, r1, s1_.size())) + { + return Operation::process + ( + s0_, + s1_.substr(r0, (r1 - r0) + 1) + ); + } + else + return T(0); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return Operation::type(); + } + + inline operator_type operation() const exprtk_override + { + return Operation::operation(); + } + + inline std::string& s0() + { + return s0_; + } + + inline std::string& s1() + { + return s1_; + } + + protected: + + SType0 s0_; + SType1 s1_; + RangePack rp1_; + + private: + + str_xoxr_node(const node_type&) exprtk_delete; + node_type& operator=(const node_type&) exprtk_delete; + }; + + template + class str_xroxr_node exprtk_final : public sos_base_node + { + public: + + typedef expression_node* expression_ptr; + typedef Operation operation_t; + typedef str_xroxr_node node_type; + + // string-range op string-range node + explicit str_xroxr_node(SType0 p0, SType1 p1, RangePack rp0, RangePack rp1) + : s0_ (p0 ) + , s1_ (p1 ) + , rp0_(rp0) + , rp1_(rp1) + {} + + ~str_xroxr_node() exprtk_override + { + rp0_.free(); + rp1_.free(); + } + + inline T value() const exprtk_override + { + std::size_t r0_0 = 0; + std::size_t r0_1 = 0; + std::size_t r1_0 = 0; + std::size_t r1_1 = 0; + + if ( + rp0_(r0_0, r1_0, s0_.size()) && + rp1_(r0_1, r1_1, s1_.size()) + ) + { + return Operation::process + ( + s0_.substr(r0_0, (r1_0 - r0_0) + 1), + s1_.substr(r0_1, (r1_1 - r0_1) + 1) + ); + } + else + return T(0); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return Operation::type(); + } + + inline operator_type operation() const exprtk_override + { + return Operation::operation(); + } + + inline std::string& s0() + { + return s0_; + } + + inline std::string& s1() + { + return s1_; + } + + protected: + + SType0 s0_; + SType1 s1_; + RangePack rp0_; + RangePack rp1_; + + private: + + str_xroxr_node(const node_type&) exprtk_delete; + node_type& operator=(const node_type&) exprtk_delete; + }; + + template + class str_sogens_node exprtk_final : public binary_node + { + public: + + typedef expression_node * expression_ptr; + typedef string_base_node* str_base_ptr; + typedef range_pack range_t; + typedef range_t* range_ptr; + typedef range_interface irange_t; + typedef irange_t* irange_ptr; + + using binary_node::branch; + + str_sogens_node(const operator_type& opr, + expression_ptr branch0, + expression_ptr branch1) + : binary_node(opr, branch0, branch1) + , str0_base_ptr_ (0) + , str1_base_ptr_ (0) + , str0_range_ptr_(0) + , str1_range_ptr_(0) + , initialised_ (false) + { + if (is_generally_string_node(branch(0))) + { + str0_base_ptr_ = dynamic_cast(branch(0)); + + if (0 == str0_base_ptr_) + return; + + irange_ptr range = dynamic_cast(branch(0)); + + if (0 == range) + return; + + str0_range_ptr_ = &(range->range_ref()); + } + + if (is_generally_string_node(branch(1))) + { + str1_base_ptr_ = dynamic_cast(branch(1)); + + if (0 == str1_base_ptr_) + return; + + irange_ptr range = dynamic_cast(branch(1)); + + if (0 == range) + return; + + str1_range_ptr_ = &(range->range_ref()); + } + + initialised_ = + str0_base_ptr_ && + str1_base_ptr_ && + str0_range_ptr_ && + str1_range_ptr_; + + assert(valid()); + } + + inline T value() const exprtk_override + { + branch(0)->value(); + branch(1)->value(); + + std::size_t str0_r0 = 0; + std::size_t str0_r1 = 0; + + std::size_t str1_r0 = 0; + std::size_t str1_r1 = 0; + + const range_t& range0 = (*str0_range_ptr_); + const range_t& range1 = (*str1_range_ptr_); + + if ( + range0(str0_r0, str0_r1, str0_base_ptr_->size()) && + range1(str1_r0, str1_r1, str1_base_ptr_->size()) + ) + { + return Operation::process + ( + str0_base_ptr_->str().substr(str0_r0,(str0_r1 - str0_r0)), + str1_base_ptr_->str().substr(str1_r0,(str1_r1 - str1_r0)) + ); + } + + return std::numeric_limits::quiet_NaN(); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return Operation::type(); + } + + inline bool valid() const exprtk_override + { + return initialised_; + } + + private: + + str_sogens_node(const str_sogens_node&) exprtk_delete; + str_sogens_node& operator=(const str_sogens_node&) exprtk_delete; + + str_base_ptr str0_base_ptr_; + str_base_ptr str1_base_ptr_; + range_ptr str0_range_ptr_; + range_ptr str1_range_ptr_; + bool initialised_; + }; + + template + class sosos_node exprtk_final : public sosos_base_node + { + public: + + typedef expression_node* expression_ptr; + typedef Operation operation_t; + typedef sosos_node node_type; + + // string op string op string node + explicit sosos_node(SType0 p0, SType1 p1, SType2 p2) + : s0_(p0) + , s1_(p1) + , s2_(p2) + {} + + inline T value() const exprtk_override + { + return Operation::process(s0_, s1_, s2_); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return Operation::type(); + } + + inline operator_type operation() const exprtk_override + { + return Operation::operation(); + } + + inline std::string& s0() + { + return s0_; + } + + inline std::string& s1() + { + return s1_; + } + + inline std::string& s2() + { + return s2_; + } + + protected: + + SType0 s0_; + SType1 s1_; + SType2 s2_; + + private: + + sosos_node(const node_type&) exprtk_delete; + node_type& operator=(const node_type&) exprtk_delete; + }; + #endif + + template + class ipow_node exprtk_final: public expression_node + { + public: + + typedef expression_node* expression_ptr; + typedef PowOp operation_t; + + explicit ipow_node(const T& v) + : v_(v) + {} + + inline T value() const exprtk_override + { + return PowOp::result(v_); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_ipow; + } + + private: + + ipow_node(const ipow_node&) exprtk_delete; + ipow_node& operator=(const ipow_node&) exprtk_delete; + + const T& v_; + }; + + template + class bipow_node exprtk_final : public expression_node + { + public: + + typedef expression_node* expression_ptr; + typedef std::pair branch_t; + typedef PowOp operation_t; + + explicit bipow_node(expression_ptr branch) + { + construct_branch_pair(branch_, branch); + assert(valid()); + } + + inline T value() const exprtk_override + { + return PowOp::result(branch_.first->value()); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_ipow; + } + + inline bool valid() const exprtk_override + { + return branch_.first && branch_.first->valid(); + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(branch_, node_delete_list); + } + + std::size_t node_depth() const exprtk_override + { + return expression_node::ndb_t::compute_node_depth(branch_); + } + + private: + + bipow_node(const bipow_node&) exprtk_delete; + bipow_node& operator=(const bipow_node&) exprtk_delete; + + branch_t branch_; + }; + + template + class ipowinv_node exprtk_final : public expression_node + { + public: + + typedef expression_node* expression_ptr; + typedef PowOp operation_t; + + explicit ipowinv_node(const T& v) + : v_(v) + {} + + inline T value() const exprtk_override + { + return (T(1) / PowOp::result(v_)); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_ipowinv; + } + + private: + + ipowinv_node(const ipowinv_node&) exprtk_delete; + ipowinv_node& operator=(const ipowinv_node&) exprtk_delete; + + const T& v_; + }; + + template + class bipowinv_node exprtk_final : public expression_node + { + public: + + typedef expression_node* expression_ptr; + typedef std::pair branch_t; + typedef PowOp operation_t; + + explicit bipowinv_node(expression_ptr branch) + { + construct_branch_pair(branch_, branch); + assert(valid()); + } + + inline T value() const exprtk_override + { + return (T(1) / PowOp::result(branch_.first->value())); + } + + inline typename expression_node::node_type type() const exprtk_override + { + return expression_node::e_ipowinv; + } + + inline bool valid() const exprtk_override + { + return branch_.first && branch_.first->valid(); + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) exprtk_override + { + expression_node::ndb_t::collect(branch_, node_delete_list); + } + + std::size_t node_depth() const exprtk_override + { + return expression_node::ndb_t::compute_node_depth(branch_); + } + + private: + + bipowinv_node(const bipowinv_node&) exprtk_delete; + bipowinv_node& operator=(const bipowinv_node&) exprtk_delete; + + branch_t branch_; + }; + + template + inline bool is_vov_node(const expression_node* node) + { + return (0 != dynamic_cast*>(node)); + } + + template + inline bool is_cov_node(const expression_node* node) + { + return (0 != dynamic_cast*>(node)); + } + + template + inline bool is_voc_node(const expression_node* node) + { + return (0 != dynamic_cast*>(node)); + } + + template + inline bool is_cob_node(const expression_node* node) + { + return (0 != dynamic_cast*>(node)); + } + + template + inline bool is_boc_node(const expression_node* node) + { + return (0 != dynamic_cast*>(node)); + } + + template + inline bool is_t0ot1ot2_node(const expression_node* node) + { + return (0 != dynamic_cast*>(node)); + } + + template + inline bool is_t0ot1ot2ot3_node(const expression_node* node) + { + return (0 != dynamic_cast*>(node)); + } + + template + inline bool is_uv_node(const expression_node* node) + { + return (0 != dynamic_cast*>(node)); + } + + template + inline bool is_string_node(const expression_node* node) + { + return node && (expression_node::e_stringvar == node->type()); + } + + template + inline bool is_string_range_node(const expression_node* node) + { + return node && (expression_node::e_stringvarrng == node->type()); + } + + template + inline bool is_const_string_node(const expression_node* node) + { + return node && (expression_node::e_stringconst == node->type()); + } + + template + inline bool is_const_string_range_node(const expression_node* node) + { + return node && (expression_node::e_cstringvarrng == node->type()); + } + + template + inline bool is_string_assignment_node(const expression_node* node) + { + return node && (expression_node::e_strass == node->type()); + } + + template + inline bool is_string_concat_node(const expression_node* node) + { + return node && (expression_node::e_strconcat == node->type()); + } + + template + inline bool is_string_function_node(const expression_node* node) + { + return node && (expression_node::e_strfunction == node->type()); + } + + template + inline bool is_string_condition_node(const expression_node* node) + { + return node && (expression_node::e_strcondition == node->type()); + } + + template + inline bool is_string_ccondition_node(const expression_node* node) + { + return node && (expression_node::e_strccondition == node->type()); + } + + template + inline bool is_string_vararg_node(const expression_node* node) + { + return node && (expression_node::e_stringvararg == node->type()); + } + + template + inline bool is_genricstring_range_node(const expression_node* node) + { + return node && (expression_node::e_strgenrange == node->type()); + } + + template + inline bool is_generally_string_node(const expression_node* node) + { + if (node) + { + switch (node->type()) + { + case expression_node::e_stringvar : + case expression_node::e_stringconst : + case expression_node::e_stringvarrng : + case expression_node::e_cstringvarrng : + case expression_node::e_strgenrange : + case expression_node::e_strass : + case expression_node::e_strconcat : + case expression_node::e_strfunction : + case expression_node::e_strcondition : + case expression_node::e_strccondition : + case expression_node::e_stringvararg : return true; + default : return false; + } + } + + return false; + } + + template + inline bool is_loop_node(const expression_node* node) + { + if (node) + { + switch (node->type()) + { + case expression_node::e_for : + case expression_node::e_repeat : + case expression_node::e_while : return true; + default : return false; + } + } + + return false; + } + + template + inline bool is_block_node(const expression_node* node) + { + if (node) + { + if (is_loop_node(node)) + { + return true; + } + + switch (node->type()) + { + case expression_node::e_conditional : + case expression_node::e_mswitch : + case expression_node::e_switch : + case expression_node::e_vararg : return true; + default : return false; + } + } + + return false; + } + + class node_allocator + { + public: + + template + inline expression_node* allocate(OpType& operation, ExprNode (&branch)[1]) + { + expression_node* result = + allocate(operation, branch[0]); + result->node_depth(); + return result; + } + + template + inline expression_node* allocate(OpType& operation, ExprNode (&branch)[2]) + { + expression_node* result = + allocate(operation, branch[0], branch[1]); + result->node_depth(); + return result; + } + + template + inline expression_node* allocate(OpType& operation, ExprNode (&branch)[3]) + { + expression_node* result = + allocate(operation, branch[0], branch[1], branch[2]); + result->node_depth(); + return result; + } + + template + inline expression_node* allocate(OpType& operation, ExprNode (&branch)[4]) + { + expression_node* result = + allocate(operation, branch[0], branch[1], branch[2], branch[3]); + result->node_depth(); + return result; + } + + template + inline expression_node* allocate(OpType& operation, ExprNode (&branch)[5]) + { + expression_node* result = + allocate(operation, branch[0],branch[1], branch[2], branch[3], branch[4]); + result->node_depth(); + return result; + } + + template + inline expression_node* allocate(OpType& operation, ExprNode (&branch)[6]) + { + expression_node* result = + allocate(operation, branch[0], branch[1], branch[2], branch[3], branch[4], branch[5]); + result->node_depth(); + return result; + } + + template + inline expression_node* allocate() const + { + return (new node_type()); + } + + template class Sequence> + inline expression_node* allocate(const Sequence& seq) const + { + expression_node* + result = (new node_type(seq)); + result->node_depth(); + return result; + } + + template + inline expression_node* allocate(T1& t1) const + { + expression_node* + result = (new node_type(t1)); + result->node_depth(); + return result; + } + + template + inline expression_node* allocate_c(const T1& t1) const + { + expression_node* + result = (new node_type(t1)); + result->node_depth(); + return result; + } + + template + inline expression_node* allocate(const T1& t1, const T2& t2) const + { + expression_node* + result = (new node_type(t1, t2)); + result->node_depth(); + return result; + } + + template + inline expression_node* allocate_cr(const T1& t1, T2& t2) const + { + expression_node* + result = (new node_type(t1, t2)); + result->node_depth(); + return result; + } + + template + inline expression_node* allocate_rc(T1& t1, const T2& t2) const + { + expression_node* + result = (new node_type(t1, t2)); + result->node_depth(); + return result; + } + + template + inline expression_node* allocate_rr(T1& t1, T2& t2) const + { + expression_node* + result = (new node_type(t1, t2)); + result->node_depth(); + return result; + } + + template + inline expression_node* allocate_tt(T1 t1, T2 t2) const + { + expression_node* + result = (new node_type(t1, t2)); + result->node_depth(); + return result; + } + + template + inline expression_node* allocate_ttt(T1 t1, T2 t2, T3 t3) const + { + expression_node* + result = (new node_type(t1, t2, t3)); + result->node_depth(); + return result; + } + + template + inline expression_node* allocate_tttt(T1 t1, T2 t2, T3 t3, T4 t4) const + { + expression_node* + result = (new node_type(t1, t2, t3, t4)); + result->node_depth(); + return result; + } + + template + inline expression_node* allocate_rrr(T1& t1, T2& t2, T3& t3) const + { + expression_node* + result = (new node_type(t1, t2, t3)); + result->node_depth(); + return result; + } + + template + inline expression_node* allocate_rrrr(T1& t1, T2& t2, T3& t3, T4& t4) const + { + expression_node* + result = (new node_type(t1, t2, t3, t4)); + result->node_depth(); + return result; + } + + template + inline expression_node* allocate_rrrrr(T1& t1, T2& t2, T3& t3, T4& t4, T5& t5) const + { + expression_node* + result = (new node_type(t1, t2, t3, t4, t5)); + result->node_depth(); + return result; + } + + template + inline expression_node* allocate(const T1& t1, const T2& t2, + const T3& t3) const + { + expression_node* + result = (new node_type(t1, t2, t3)); + result->node_depth(); + return result; + } + + template + inline expression_node* allocate(const T1& t1, const T2& t2, + const T3& t3, const T4& t4) const + { + expression_node* + result = (new node_type(t1, t2, t3, t4)); + result->node_depth(); + return result; + } + + template + inline expression_node* allocate(const T1& t1, const T2& t2, + const T3& t3, const T4& t4, + const T5& t5) const + { + expression_node* + result = (new node_type(t1, t2, t3, t4, t5)); + result->node_depth(); + return result; + } + + template + inline expression_node* allocate(const T1& t1, const T2& t2, + const T3& t3, const T4& t4, + const T5& t5, const T6& t6) const + { + expression_node* + result = (new node_type(t1, t2, t3, t4, t5, t6)); + result->node_depth(); + return result; + } + + template + inline expression_node* allocate(const T1& t1, const T2& t2, + const T3& t3, const T4& t4, + const T5& t5, const T6& t6, + const T7& t7) const + { + expression_node* + result = (new node_type(t1, t2, t3, t4, t5, t6, t7)); + result->node_depth(); + return result; + } + + template + inline expression_node* allocate(const T1& t1, const T2& t2, + const T3& t3, const T4& t4, + const T5& t5, const T6& t6, + const T7& t7, const T8& t8) const + { + expression_node* + result = (new node_type(t1, t2, t3, t4, t5, t6, t7, t8)); + result->node_depth(); + return result; + } + + template + inline expression_node* allocate(const T1& t1, const T2& t2, + const T3& t3, const T4& t4, + const T5& t5, const T6& t6, + const T7& t7, const T8& t8, + const T9& t9) const + { + expression_node* + result = (new node_type(t1, t2, t3, t4, t5, t6, t7, t8, t9)); + result->node_depth(); + return result; + } + + template + inline expression_node* allocate(const T1& t1, const T2& t2, + const T3& t3, const T4& t4, + const T5& t5, const T6& t6, + const T7& t7, const T8& t8, + const T9& t9, const T10& t10) const + { + expression_node* + result = (new node_type(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10)); + result->node_depth(); + return result; + } + + template + inline expression_node* allocate_type(T1 t1, T2 t2, T3 t3) const + { + expression_node* + result = (new node_type(t1, t2, t3)); + result->node_depth(); + return result; + } + + template + inline expression_node* allocate_type(T1 t1, T2 t2, + T3 t3, T4 t4) const + { + expression_node* + result = (new node_type(t1, t2, t3, t4)); + result->node_depth(); + return result; + } + + template + inline expression_node* allocate_type(T1 t1, T2 t2, + T3 t3, T4 t4, + T5 t5) const + { + expression_node* + result = (new node_type(t1, t2, t3, t4, t5)); + result->node_depth(); + return result; + } + + template + inline expression_node* allocate_type(T1 t1, T2 t2, + T3 t3, T4 t4, + T5 t5, T6 t6) const + { + expression_node* + result = (new node_type(t1, t2, t3, t4, t5, t6)); + result->node_depth(); + return result; + } + + template + inline expression_node* allocate_type(T1 t1, T2 t2, + T3 t3, T4 t4, + T5 t5, T6 t6, + T7 t7) const + { + expression_node* + result = (new node_type(t1, t2, t3, t4, t5, t6, t7)); + result->node_depth(); + return result; + } + + template + void inline free(expression_node*& e) const + { + exprtk_debug(("node_allocator::free() - deleting expression_node " + "type: %03d addr: %p\n", + static_cast(e->type()), + reinterpret_cast(e))); + delete e; + e = 0; + } + }; + + inline void load_operations_map(std::multimap& m) + { + #define register_op(Symbol, Type, Args) \ + m.insert(std::make_pair(std::string(Symbol),details::base_operation_t(Type,Args))); \ + + register_op("abs" , e_abs , 1) + register_op("acos" , e_acos , 1) + register_op("acosh" , e_acosh , 1) + register_op("asin" , e_asin , 1) + register_op("asinh" , e_asinh , 1) + register_op("atan" , e_atan , 1) + register_op("atanh" , e_atanh , 1) + register_op("ceil" , e_ceil , 1) + register_op("cos" , e_cos , 1) + register_op("cosh" , e_cosh , 1) + register_op("exp" , e_exp , 1) + register_op("expm1" , e_expm1 , 1) + register_op("floor" , e_floor , 1) + register_op("log" , e_log , 1) + register_op("log10" , e_log10 , 1) + register_op("log2" , e_log2 , 1) + register_op("log1p" , e_log1p , 1) + register_op("round" , e_round , 1) + register_op("sin" , e_sin , 1) + register_op("sinc" , e_sinc , 1) + register_op("sinh" , e_sinh , 1) + register_op("sec" , e_sec , 1) + register_op("csc" , e_csc , 1) + register_op("sqrt" , e_sqrt , 1) + register_op("tan" , e_tan , 1) + register_op("tanh" , e_tanh , 1) + register_op("cot" , e_cot , 1) + register_op("rad2deg" , e_r2d , 1) + register_op("deg2rad" , e_d2r , 1) + register_op("deg2grad" , e_d2g , 1) + register_op("grad2deg" , e_g2d , 1) + register_op("sgn" , e_sgn , 1) + register_op("not" , e_notl , 1) + register_op("erf" , e_erf , 1) + register_op("erfc" , e_erfc , 1) + register_op("ncdf" , e_ncdf , 1) + register_op("frac" , e_frac , 1) + register_op("trunc" , e_trunc , 1) + register_op("atan2" , e_atan2 , 2) + register_op("mod" , e_mod , 2) + register_op("logn" , e_logn , 2) + register_op("pow" , e_pow , 2) + register_op("root" , e_root , 2) + register_op("roundn" , e_roundn , 2) + register_op("equal" , e_equal , 2) + register_op("not_equal" , e_nequal , 2) + register_op("hypot" , e_hypot , 2) + register_op("shr" , e_shr , 2) + register_op("shl" , e_shl , 2) + register_op("clamp" , e_clamp , 3) + register_op("iclamp" , e_iclamp , 3) + register_op("inrange" , e_inrange , 3) + #undef register_op + } + + } // namespace details + + class function_traits + { + public: + + function_traits() + : allow_zero_parameters_(false) + , has_side_effects_(true) + , min_num_args_(0) + , max_num_args_(std::numeric_limits::max()) + {} + + inline bool& allow_zero_parameters() + { + return allow_zero_parameters_; + } + + inline bool& has_side_effects() + { + return has_side_effects_; + } + + std::size_t& min_num_args() + { + return min_num_args_; + } + + std::size_t& max_num_args() + { + return max_num_args_; + } + + private: + + bool allow_zero_parameters_; + bool has_side_effects_; + std::size_t min_num_args_; + std::size_t max_num_args_; + }; + + template + void enable_zero_parameters(FunctionType& func) + { + func.allow_zero_parameters() = true; + + if (0 != func.min_num_args()) + { + func.min_num_args() = 0; + } + } + + template + void disable_zero_parameters(FunctionType& func) + { + func.allow_zero_parameters() = false; + } + + template + void enable_has_side_effects(FunctionType& func) + { + func.has_side_effects() = true; + } + + template + void disable_has_side_effects(FunctionType& func) + { + func.has_side_effects() = false; + } + + template + void set_min_num_args(FunctionType& func, const std::size_t& num_args) + { + func.min_num_args() = num_args; + + if ((0 != func.min_num_args()) && func.allow_zero_parameters()) + func.allow_zero_parameters() = false; + } + + template + void set_max_num_args(FunctionType& func, const std::size_t& num_args) + { + func.max_num_args() = num_args; + } + + template + class ifunction : public function_traits + { + public: + + explicit ifunction(const std::size_t& pc) + : param_count(pc) + {} + + virtual ~ifunction() + {} + + #define empty_method_body(N) \ + { \ + exprtk_debug(("ifunction::operator() - Operator(" #N ") has not been overridden\n")); \ + return std::numeric_limits::quiet_NaN(); \ + } \ + + inline virtual T operator() () + empty_method_body(0) + + inline virtual T operator() (const T&) + empty_method_body(1) + + inline virtual T operator() (const T&,const T&) + empty_method_body(2) + + inline virtual T operator() (const T&, const T&, const T&) + empty_method_body(3) + + inline virtual T operator() (const T&, const T&, const T&, const T&) + empty_method_body(4) + + inline virtual T operator() (const T&, const T&, const T&, const T&, const T&) + empty_method_body(5) + + inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&) + empty_method_body(6) + + inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&) + empty_method_body(7) + + inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&) + empty_method_body(8) + + inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&) + empty_method_body(9) + + inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&) + empty_method_body(10) + + inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, + const T&) + empty_method_body(11) + + inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, + const T&, const T&) + empty_method_body(12) + + inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, + const T&, const T&, const T&) + empty_method_body(13) + + inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, + const T&, const T&, const T&, const T&) + empty_method_body(14) + + inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, + const T&, const T&, const T&, const T&, const T&) + empty_method_body(15) + + inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, + const T&, const T&, const T&, const T&, const T&, const T&) + empty_method_body(16) + + inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, + const T&, const T&, const T&, const T&, const T&, const T&, const T&) + empty_method_body(17) + + inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, + const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&) + empty_method_body(18) + + inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, + const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&) + empty_method_body(19) + + inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, + const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&) + empty_method_body(20) + + #undef empty_method_body + + std::size_t param_count; + }; + + template + class ivararg_function : public function_traits + { + public: + + virtual ~ivararg_function() + {} + + inline virtual T operator() (const std::vector&) + { + exprtk_debug(("ivararg_function::operator() - Operator has not been overridden\n")); + return std::numeric_limits::quiet_NaN(); + } + }; + + template + class igeneric_function : public function_traits + { + public: + + enum return_type + { + e_rtrn_scalar = 0, + e_rtrn_string = 1, + e_rtrn_overload = 2 + }; + + typedef T type; + typedef type_store generic_type; + typedef typename generic_type::parameter_list parameter_list_t; + + explicit igeneric_function(const std::string& param_seq = "", const return_type rtr_type = e_rtrn_scalar) + : parameter_sequence(param_seq) + , rtrn_type(rtr_type) + {} + + virtual ~igeneric_function() + {} + + #define igeneric_function_empty_body(N) \ + { \ + exprtk_debug(("igeneric_function::operator() - Operator(" #N ") has not been overridden\n")); \ + return std::numeric_limits::quiet_NaN(); \ + } \ + + // f(i_0,i_1,....,i_N) --> Scalar + inline virtual T operator() (parameter_list_t) + igeneric_function_empty_body(1) + + // f(i_0,i_1,....,i_N) --> String + inline virtual T operator() (std::string&, parameter_list_t) + igeneric_function_empty_body(2) + + // f(psi,i_0,i_1,....,i_N) --> Scalar + inline virtual T operator() (const std::size_t&, parameter_list_t) + igeneric_function_empty_body(3) + + // f(psi,i_0,i_1,....,i_N) --> String + inline virtual T operator() (const std::size_t&, std::string&, parameter_list_t) + igeneric_function_empty_body(4) + + #undef igeneric_function_empty_body + + std::string parameter_sequence; + return_type rtrn_type; + }; + + #ifndef exprtk_disable_string_capabilities + template + class stringvar_base + { + public: + + typedef typename details::stringvar_node stringvar_node_t; + + stringvar_base(const std::string& name, stringvar_node_t* svn) + : name_(name) + , string_varnode_(svn) + {} + + bool valid() const + { + return !name_.empty() && (0 != string_varnode_); + } + + std::string name() const + { + assert(string_varnode_); + return name_; + } + + void rebase(std::string& s) + { + assert(string_varnode_); + string_varnode_->rebase(s); + } + + private: + + std::string name_; + stringvar_node_t* string_varnode_; + }; + #endif + + template class parser; + template class expression_helper; + + template + class symbol_table + { + public: + + enum symtab_mutability_type + { + e_unknown = 0, + e_mutable = 1, + e_immutable = 2 + }; + + typedef T (*ff00_functor)(); + typedef T (*ff01_functor)(T); + typedef T (*ff02_functor)(T, T); + typedef T (*ff03_functor)(T, T, T); + typedef T (*ff04_functor)(T, T, T, T); + typedef T (*ff05_functor)(T, T, T, T, T); + typedef T (*ff06_functor)(T, T, T, T, T, T); + typedef T (*ff07_functor)(T, T, T, T, T, T, T); + typedef T (*ff08_functor)(T, T, T, T, T, T, T, T); + typedef T (*ff09_functor)(T, T, T, T, T, T, T, T, T); + typedef T (*ff10_functor)(T, T, T, T, T, T, T, T, T, T); + typedef T (*ff11_functor)(T, T, T, T, T, T, T, T, T, T, T); + typedef T (*ff12_functor)(T, T, T, T, T, T, T, T, T, T, T, T); + typedef T (*ff13_functor)(T, T, T, T, T, T, T, T, T, T, T, T, T); + typedef T (*ff14_functor)(T, T, T, T, T, T, T, T, T, T, T, T, T, T); + typedef T (*ff15_functor)(T, T, T, T, T, T, T, T, T, T, T, T, T, T, T); + + protected: + + struct freefunc00 exprtk_final : public exprtk::ifunction + { + using exprtk::ifunction::operator(); + + explicit freefunc00(ff00_functor ff) : exprtk::ifunction(0), f(ff) {} + inline T operator() () exprtk_override + { return f(); } + ff00_functor f; + }; + + struct freefunc01 exprtk_final : public exprtk::ifunction + { + using exprtk::ifunction::operator(); + + explicit freefunc01(ff01_functor ff) : exprtk::ifunction(1), f(ff) {} + inline T operator() (const T& v0) exprtk_override + { return f(v0); } + ff01_functor f; + }; + + struct freefunc02 exprtk_final : public exprtk::ifunction + { + using exprtk::ifunction::operator(); + + explicit freefunc02(ff02_functor ff) : exprtk::ifunction(2), f(ff) {} + inline T operator() (const T& v0, const T& v1) exprtk_override + { return f(v0, v1); } + ff02_functor f; + }; + + struct freefunc03 exprtk_final : public exprtk::ifunction + { + using exprtk::ifunction::operator(); + + explicit freefunc03(ff03_functor ff) : exprtk::ifunction(3), f(ff) {} + inline T operator() (const T& v0, const T& v1, const T& v2) exprtk_override + { return f(v0, v1, v2); } + ff03_functor f; + }; + + struct freefunc04 exprtk_final : public exprtk::ifunction + { + using exprtk::ifunction::operator(); + + explicit freefunc04(ff04_functor ff) : exprtk::ifunction(4), f(ff) {} + inline T operator() (const T& v0, const T& v1, const T& v2, const T& v3) exprtk_override + { return f(v0, v1, v2, v3); } + ff04_functor f; + }; + + struct freefunc05 : public exprtk::ifunction + { + using exprtk::ifunction::operator(); + + explicit freefunc05(ff05_functor ff) : exprtk::ifunction(5), f(ff) {} + inline T operator() (const T& v0, const T& v1, const T& v2, const T& v3, const T& v4) exprtk_override + { return f(v0, v1, v2, v3, v4); } + ff05_functor f; + }; + + struct freefunc06 exprtk_final : public exprtk::ifunction + { + using exprtk::ifunction::operator(); + + explicit freefunc06(ff06_functor ff) : exprtk::ifunction(6), f(ff) {} + inline T operator() (const T& v0, const T& v1, const T& v2, const T& v3, const T& v4, const T& v5) exprtk_override + { return f(v0, v1, v2, v3, v4, v5); } + ff06_functor f; + }; + + struct freefunc07 exprtk_final : public exprtk::ifunction + { + using exprtk::ifunction::operator(); + + explicit freefunc07(ff07_functor ff) : exprtk::ifunction(7), f(ff) {} + inline T operator() (const T& v0, const T& v1, const T& v2, const T& v3, const T& v4, + const T& v5, const T& v6) exprtk_override + { return f(v0, v1, v2, v3, v4, v5, v6); } + ff07_functor f; + }; + + struct freefunc08 exprtk_final : public exprtk::ifunction + { + using exprtk::ifunction::operator(); + + explicit freefunc08(ff08_functor ff) : exprtk::ifunction(8), f(ff) {} + inline T operator() (const T& v0, const T& v1, const T& v2, const T& v3, const T& v4, + const T& v5, const T& v6, const T& v7) exprtk_override + { return f(v0, v1, v2, v3, v4, v5, v6, v7); } + ff08_functor f; + }; + + struct freefunc09 exprtk_final : public exprtk::ifunction + { + using exprtk::ifunction::operator(); + + explicit freefunc09(ff09_functor ff) : exprtk::ifunction(9), f(ff) {} + inline T operator() (const T& v0, const T& v1, const T& v2, const T& v3, const T& v4, + const T& v5, const T& v6, const T& v7, const T& v8) exprtk_override + { return f(v0, v1, v2, v3, v4, v5, v6, v7, v8); } + ff09_functor f; + }; + + struct freefunc10 exprtk_final : public exprtk::ifunction + { + using exprtk::ifunction::operator(); + + explicit freefunc10(ff10_functor ff) : exprtk::ifunction(10), f(ff) {} + inline T operator() (const T& v0, const T& v1, const T& v2, const T& v3, const T& v4, + const T& v5, const T& v6, const T& v7, const T& v8, const T& v9) exprtk_override + { return f(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9); } + ff10_functor f; + }; + + struct freefunc11 exprtk_final : public exprtk::ifunction + { + using exprtk::ifunction::operator(); + + explicit freefunc11(ff11_functor ff) : exprtk::ifunction(11), f(ff) {} + inline T operator() (const T& v0, const T& v1, const T& v2, const T& v3, const T& v4, + const T& v5, const T& v6, const T& v7, const T& v8, const T& v9, const T& v10) exprtk_override + { return f(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10); } + ff11_functor f; + }; + + struct freefunc12 exprtk_final : public exprtk::ifunction + { + using exprtk::ifunction::operator(); + + explicit freefunc12(ff12_functor ff) : exprtk::ifunction(12), f(ff) {} + inline T operator() (const T& v00, const T& v01, const T& v02, const T& v03, const T& v04, + const T& v05, const T& v06, const T& v07, const T& v08, const T& v09, + const T& v10, const T& v11) exprtk_override + { return f(v00, v01, v02, v03, v04, v05, v06, v07, v08, v09, v10, v11); } + ff12_functor f; + }; + + struct freefunc13 exprtk_final : public exprtk::ifunction + { + using exprtk::ifunction::operator(); + + explicit freefunc13(ff13_functor ff) : exprtk::ifunction(13), f(ff) {} + inline T operator() (const T& v00, const T& v01, const T& v02, const T& v03, const T& v04, + const T& v05, const T& v06, const T& v07, const T& v08, const T& v09, + const T& v10, const T& v11, const T& v12) exprtk_override + { return f(v00, v01, v02, v03, v04, v05, v06, v07, v08, v09, v10, v11, v12); } + ff13_functor f; + }; + + struct freefunc14 exprtk_final : public exprtk::ifunction + { + using exprtk::ifunction::operator(); + + explicit freefunc14(ff14_functor ff) : exprtk::ifunction(14), f(ff) {} + inline T operator() (const T& v00, const T& v01, const T& v02, const T& v03, const T& v04, + const T& v05, const T& v06, const T& v07, const T& v08, const T& v09, + const T& v10, const T& v11, const T& v12, const T& v13) exprtk_override + { return f(v00, v01, v02, v03, v04, v05, v06, v07, v08, v09, v10, v11, v12, v13); } + ff14_functor f; + }; + + struct freefunc15 exprtk_final : public exprtk::ifunction + { + using exprtk::ifunction::operator(); + + explicit freefunc15(ff15_functor ff) : exprtk::ifunction(15), f(ff) {} + inline T operator() (const T& v00, const T& v01, const T& v02, const T& v03, const T& v04, + const T& v05, const T& v06, const T& v07, const T& v08, const T& v09, + const T& v10, const T& v11, const T& v12, const T& v13, const T& v14) exprtk_override + { return f(v00, v01, v02, v03, v04, v05, v06, v07, v08, v09, v10, v11, v12, v13, v14); } + ff15_functor f; + }; + + template + struct type_store + { + typedef details::expression_node* expression_ptr; + typedef typename details::variable_node variable_node_t; + typedef ifunction ifunction_t; + typedef ivararg_function ivararg_function_t; + typedef igeneric_function igeneric_function_t; + typedef details::vector_holder vector_t; + #ifndef exprtk_disable_string_capabilities + typedef typename details::stringvar_node stringvar_node_t; + #endif + + typedef Type type_t; + typedef type_t* type_ptr; + typedef std::pair type_pair_t; + typedef std::map type_map_t; + typedef typename type_map_t::iterator tm_itr_t; + typedef typename type_map_t::const_iterator tm_const_itr_t; + + enum { lut_size = 256 }; + + type_map_t map; + std::size_t size; + + type_store() + : size(0) + {} + + struct deleter + { + #define exprtk_define_process(Type) \ + static inline void process(std::pair& n) \ + { \ + delete n.second; \ + } \ + + exprtk_define_process(variable_node_t ) + exprtk_define_process(vector_t ) + #ifndef exprtk_disable_string_capabilities + exprtk_define_process(stringvar_node_t) + #endif + + #undef exprtk_define_process + + template + static inline void process(std::pair&) + {} + }; + + inline bool symbol_exists(const std::string& symbol_name) const + { + if (symbol_name.empty()) + return false; + else if (map.end() != map.find(symbol_name)) + return true; + else + return false; + } + + template + inline std::string entity_name(const PtrType& ptr) const + { + if (map.empty()) + return std::string(); + + tm_const_itr_t itr = map.begin(); + + while (map.end() != itr) + { + if (itr->second.second == ptr) + { + return itr->first; + } + else + ++itr; + } + + return std::string(); + } + + inline bool is_constant(const std::string& symbol_name) const + { + if (symbol_name.empty()) + return false; + else + { + const tm_const_itr_t itr = map.find(symbol_name); + + if (map.end() == itr) + return false; + else + return (*itr).second.first; + } + } + + template + inline bool add_impl(const std::string& symbol_name, RType t, const bool is_const) + { + if (symbol_name.size() > 1) + { + for (std::size_t i = 0; i < details::reserved_symbols_size; ++i) + { + if (details::imatch(symbol_name, details::reserved_symbols[i])) + { + return false; + } + } + } + + const tm_itr_t itr = map.find(symbol_name); + + if (map.end() == itr) + { + map[symbol_name] = Tie::make(t,is_const); + ++size; + } + + return true; + } + + struct tie_array + { + static inline std::pair make(std::pair v, const bool is_const = false) + { + return std::make_pair(is_const, new vector_t(v.first, v.second)); + } + }; + + struct tie_stdvec + { + template + static inline std::pair make(std::vector& v, const bool is_const = false) + { + return std::make_pair(is_const, new vector_t(v)); + } + }; + + struct tie_vecview + { + static inline std::pair make(exprtk::vector_view& v, const bool is_const = false) + { + return std::make_pair(is_const, new vector_t(v)); + } + }; + + struct tie_stddeq + { + template + static inline std::pair make(std::deque& v, const bool is_const = false) + { + return std::make_pair(is_const, new vector_t(v)); + } + }; + + template + inline bool add(const std::string& symbol_name, T (&v)[v_size], const bool is_const = false) + { + return add_impl > + (symbol_name, std::make_pair(v,v_size), is_const); + } + + inline bool add(const std::string& symbol_name, T* v, const std::size_t v_size, const bool is_const = false) + { + return add_impl > + (symbol_name, std::make_pair(v,v_size), is_const); + } + + template + inline bool add(const std::string& symbol_name, std::vector& v, const bool is_const = false) + { + return add_impl&> + (symbol_name, v, is_const); + } + + inline bool add(const std::string& symbol_name, exprtk::vector_view& v, const bool is_const = false) + { + return add_impl&> + (symbol_name, v, is_const); + } + + template + inline bool add(const std::string& symbol_name, std::deque& v, const bool is_const = false) + { + return add_impl&> + (symbol_name, v, is_const); + } + + inline bool add(const std::string& symbol_name, RawType& t_, const bool is_const = false) + { + struct tie + { + static inline std::pair make(T& t, const bool is_constant = false) + { + return std::make_pair(is_constant, new variable_node_t(t)); + } + + #ifndef exprtk_disable_string_capabilities + static inline std::pair make(std::string& t, const bool is_constant = false) + { + return std::make_pair(is_constant, new stringvar_node_t(t)); + } + #endif + + static inline std::pair make(function_t& t, const bool is_constant = false) + { + return std::make_pair(is_constant,&t); + } + + static inline std::pair make(vararg_function_t& t, const bool is_constant = false) + { + return std::make_pair(is_constant,&t); + } + + static inline std::pair make(generic_function_t& t, const bool is_constant = false) + { + return std::make_pair(is_constant,&t); + } + }; + + const tm_itr_t itr = map.find(symbol_name); + + if (map.end() == itr) + { + map[symbol_name] = tie::make(t_,is_const); + ++size; + } + + return true; + } + + inline type_ptr get(const std::string& symbol_name) const + { + const tm_const_itr_t itr = map.find(symbol_name); + + if (map.end() == itr) + return reinterpret_cast(0); + else + return itr->second.second; + } + + template + struct ptr_match + { + static inline bool test(const PtrType, const void*) + { + return false; + } + }; + + template + struct ptr_match + { + static inline bool test(const variable_node_t* p, const void* ptr) + { + exprtk_debug(("ptr_match::test() - %p <--> %p\n", reinterpret_cast(&(p->ref())), ptr)); + return (&(p->ref()) == ptr); + } + }; + + inline type_ptr get_from_varptr(const void* ptr) const + { + tm_const_itr_t itr = map.begin(); + + while (map.end() != itr) + { + type_ptr ret_ptr = itr->second.second; + + if (ptr_match::test(ret_ptr,ptr)) + { + return ret_ptr; + } + + ++itr; + } + + return type_ptr(0); + } + + inline bool remove(const std::string& symbol_name, const bool delete_node = true) + { + const tm_itr_t itr = map.find(symbol_name); + + if (map.end() != itr) + { + if (delete_node) + { + deleter::process((*itr).second); + } + + map.erase(itr); + --size; + + return true; + } + else + return false; + } + + inline RawType& type_ref(const std::string& symbol_name) + { + struct init_type + { + static inline double set(double) { return (0.0); } + static inline double set(long double) { return (0.0); } + static inline float set(float) { return (0.0f); } + static inline std::string set(std::string) { return std::string(""); } + }; + + static RawType null_type = init_type::set(RawType()); + + const tm_const_itr_t itr = map.find(symbol_name); + + if (map.end() == itr) + return null_type; + else + return itr->second.second->ref(); + } + + inline void clear(const bool delete_node = true) + { + if (!map.empty()) + { + if (delete_node) + { + tm_itr_t itr = map.begin(); + tm_itr_t end = map.end (); + + while (end != itr) + { + deleter::process((*itr).second); + ++itr; + } + } + + map.clear(); + } + + size = 0; + } + + template class Sequence> + inline std::size_t get_list(Sequence,Allocator>& list) const + { + std::size_t count = 0; + + if (!map.empty()) + { + tm_const_itr_t itr = map.begin(); + tm_const_itr_t end = map.end (); + + while (end != itr) + { + list.push_back(std::make_pair((*itr).first,itr->second.second->ref())); + ++itr; + ++count; + } + } + + return count; + } + + template class Sequence> + inline std::size_t get_list(Sequence& vlist) const + { + std::size_t count = 0; + + if (!map.empty()) + { + tm_const_itr_t itr = map.begin(); + tm_const_itr_t end = map.end (); + + while (end != itr) + { + vlist.push_back((*itr).first); + ++itr; + ++count; + } + } + + return count; + } + }; + + typedef details::expression_node* expression_ptr; + typedef typename details::variable_node variable_t; + typedef typename details::vector_holder vector_holder_t; + typedef variable_t* variable_ptr; + #ifndef exprtk_disable_string_capabilities + typedef typename details::stringvar_node stringvar_t; + typedef stringvar_t* stringvar_ptr; + #endif + typedef ifunction function_t; + typedef ivararg_function vararg_function_t; + typedef igeneric_function generic_function_t; + typedef function_t* function_ptr; + typedef vararg_function_t* vararg_function_ptr; + typedef generic_function_t* generic_function_ptr; + + static const std::size_t lut_size = 256; + + // Symbol Table Holder + struct control_block + { + struct st_data + { + type_store variable_store; + type_store function_store; + type_store vararg_function_store; + type_store generic_function_store; + type_store string_function_store; + type_store overload_function_store; + type_store vector_store; + #ifndef exprtk_disable_string_capabilities + type_store stringvar_store; + #endif + + st_data() + { + for (std::size_t i = 0; i < details::reserved_words_size; ++i) + { + reserved_symbol_table_.insert(details::reserved_words[i]); + } + + for (std::size_t i = 0; i < details::reserved_symbols_size; ++i) + { + reserved_symbol_table_.insert(details::reserved_symbols[i]); + } + } + + ~st_data() + { + for (std::size_t i = 0; i < free_function_list_.size(); ++i) + { + delete free_function_list_[i]; + } + } + + inline bool is_reserved_symbol(const std::string& symbol) const + { + return (reserved_symbol_table_.end() != reserved_symbol_table_.find(symbol)); + } + + static inline st_data* create() + { + return (new st_data); + } + + static inline void destroy(st_data*& sd) + { + delete sd; + sd = reinterpret_cast(0); + } + + std::list local_symbol_list_; + std::list local_stringvar_list_; + std::set reserved_symbol_table_; + std::vector*> free_function_list_; + }; + + control_block() + : ref_count(1) + , data_(st_data::create()) + , mutability_(e_mutable) + {} + + explicit control_block(st_data* data) + : ref_count(1) + , data_(data) + , mutability_(e_mutable) + {} + + ~control_block() + { + if (data_ && (0 == ref_count)) + { + st_data::destroy(data_); + } + } + + static inline control_block* create() + { + return (new control_block); + } + + template + static inline void destroy(control_block*& cntrl_blck, SymTab* sym_tab) + { + if (cntrl_blck) + { + if ( + (0 != cntrl_blck->ref_count) && + (0 == --cntrl_blck->ref_count) + ) + { + if (sym_tab) + sym_tab->clear(); + + delete cntrl_blck; + } + + cntrl_blck = 0; + } + } + + void set_mutability(const symtab_mutability_type mutability) + { + mutability_ = mutability; + } + + std::size_t ref_count; + st_data* data_; + symtab_mutability_type mutability_; + }; + + public: + + explicit symbol_table(const symtab_mutability_type mutability = e_mutable) + : control_block_(control_block::create()) + { + control_block_->set_mutability(mutability); + clear(); + } + + ~symbol_table() + { + exprtk::details::dump_ptr("~symbol_table", this); + control_block::destroy(control_block_, this); + } + + symbol_table(const symbol_table& st) + { + control_block_ = st.control_block_; + control_block_->ref_count++; + } + + inline symbol_table& operator=(const symbol_table& st) + { + if (this != &st) + { + control_block::destroy(control_block_,reinterpret_cast*>(0)); + + control_block_ = st.control_block_; + control_block_->ref_count++; + } + + return (*this); + } + + inline bool operator==(const symbol_table& st) const + { + return (this == &st) || (control_block_ == st.control_block_); + } + + inline symtab_mutability_type mutability() const + { + return valid() ? control_block_->mutability_ : e_unknown; + } + + inline void clear_variables(const bool delete_node = true) + { + local_data().variable_store.clear(delete_node); + } + + inline void clear_functions() + { + local_data().function_store.clear(); + } + + inline void clear_strings() + { + #ifndef exprtk_disable_string_capabilities + local_data().stringvar_store.clear(); + #endif + } + + inline void clear_vectors() + { + local_data().vector_store.clear(); + } + + inline void clear_local_constants() + { + local_data().local_symbol_list_.clear(); + } + + inline void clear() + { + if (!valid()) return; + clear_variables (); + clear_functions (); + clear_strings (); + clear_vectors (); + clear_local_constants(); + } + + inline std::size_t variable_count() const + { + if (valid()) + return local_data().variable_store.size; + else + return 0; + } + + #ifndef exprtk_disable_string_capabilities + inline std::size_t stringvar_count() const + { + if (valid()) + return local_data().stringvar_store.size; + else + return 0; + } + #endif + + inline std::size_t function_count() const + { + if (valid()) + return local_data().function_store.size; + else + return 0; + } + + inline std::size_t vector_count() const + { + if (valid()) + return local_data().vector_store.size; + else + return 0; + } + + inline variable_ptr get_variable(const std::string& variable_name) const + { + if (!valid()) + return reinterpret_cast(0); + else if (!valid_symbol(variable_name)) + return reinterpret_cast(0); + else + return local_data().variable_store.get(variable_name); + } + + inline variable_ptr get_variable(const T& var_ref) const + { + if (!valid()) + return reinterpret_cast(0); + else + return local_data().variable_store.get_from_varptr( + reinterpret_cast(&var_ref)); + } + + #ifndef exprtk_disable_string_capabilities + inline stringvar_ptr get_stringvar(const std::string& string_name) const + { + if (!valid()) + return reinterpret_cast(0); + else if (!valid_symbol(string_name)) + return reinterpret_cast(0); + else + return local_data().stringvar_store.get(string_name); + } + + inline stringvar_base get_stringvar_base(const std::string& string_name) const + { + static stringvar_base null_stringvar_base("",reinterpret_cast(0)); + if (!valid()) + return null_stringvar_base; + else if (!valid_symbol(string_name)) + return null_stringvar_base; + + stringvar_ptr stringvar = local_data().stringvar_store.get(string_name); + + if (0 == stringvar) + { + return null_stringvar_base; + } + + return stringvar_base(string_name,stringvar); + } + #endif + + inline function_ptr get_function(const std::string& function_name) const + { + if (!valid()) + return reinterpret_cast(0); + else if (!valid_symbol(function_name)) + return reinterpret_cast(0); + else + return local_data().function_store.get(function_name); + } + + inline vararg_function_ptr get_vararg_function(const std::string& vararg_function_name) const + { + if (!valid()) + return reinterpret_cast(0); + else if (!valid_symbol(vararg_function_name)) + return reinterpret_cast(0); + else + return local_data().vararg_function_store.get(vararg_function_name); + } + + inline generic_function_ptr get_generic_function(const std::string& function_name) const + { + if (!valid()) + return reinterpret_cast(0); + else if (!valid_symbol(function_name)) + return reinterpret_cast(0); + else + return local_data().generic_function_store.get(function_name); + } + + inline generic_function_ptr get_string_function(const std::string& function_name) const + { + if (!valid()) + return reinterpret_cast(0); + else if (!valid_symbol(function_name)) + return reinterpret_cast(0); + else + return local_data().string_function_store.get(function_name); + } + + inline generic_function_ptr get_overload_function(const std::string& function_name) const + { + if (!valid()) + return reinterpret_cast(0); + else if (!valid_symbol(function_name)) + return reinterpret_cast(0); + else + return local_data().overload_function_store.get(function_name); + } + + typedef vector_holder_t* vector_holder_ptr; + + inline vector_holder_ptr get_vector(const std::string& vector_name) const + { + if (!valid()) + return reinterpret_cast(0); + else if (!valid_symbol(vector_name)) + return reinterpret_cast(0); + else + return local_data().vector_store.get(vector_name); + } + + inline T& variable_ref(const std::string& symbol_name) + { + static T null_var = T(0); + if (!valid()) + return null_var; + else if (!valid_symbol(symbol_name)) + return null_var; + else + return local_data().variable_store.type_ref(symbol_name); + } + + #ifndef exprtk_disable_string_capabilities + inline std::string& stringvar_ref(const std::string& symbol_name) + { + static std::string null_stringvar; + if (!valid()) + return null_stringvar; + else if (!valid_symbol(symbol_name)) + return null_stringvar; + else + return local_data().stringvar_store.type_ref(symbol_name); + } + #endif + + inline bool is_constant_node(const std::string& symbol_name) const + { + if (!valid()) + return false; + else if (!valid_symbol(symbol_name)) + return false; + else + return local_data().variable_store.is_constant(symbol_name); + } + + #ifndef exprtk_disable_string_capabilities + inline bool is_constant_string(const std::string& symbol_name) const + { + if (!valid()) + return false; + else if (!valid_symbol(symbol_name)) + return false; + else if (!local_data().stringvar_store.symbol_exists(symbol_name)) + return false; + else + return local_data().stringvar_store.is_constant(symbol_name); + } + #endif + + inline bool create_variable(const std::string& variable_name, const T& value = T(0)) + { + if (!valid()) + return false; + else if (!valid_symbol(variable_name)) + return false; + else if (symbol_exists(variable_name)) + return false; + + local_data().local_symbol_list_.push_back(value); + T& t = local_data().local_symbol_list_.back(); + + return add_variable(variable_name,t); + } + + #ifndef exprtk_disable_string_capabilities + inline bool create_stringvar(const std::string& stringvar_name, const std::string& value = std::string("")) + { + if (!valid()) + return false; + else if (!valid_symbol(stringvar_name)) + return false; + else if (symbol_exists(stringvar_name)) + return false; + + local_data().local_stringvar_list_.push_back(value); + std::string& s = local_data().local_stringvar_list_.back(); + + return add_stringvar(stringvar_name,s); + } + #endif + + inline bool add_variable(const std::string& variable_name, T& t, const bool is_constant = false) + { + if (!valid()) + return false; + else if (!valid_symbol(variable_name)) + return false; + else if (symbol_exists(variable_name)) + return false; + else + return local_data().variable_store.add(variable_name, t, is_constant); + } + + inline bool add_constant(const std::string& constant_name, const T& value) + { + if (!valid()) + return false; + else if (!valid_symbol(constant_name)) + return false; + else if (symbol_exists(constant_name)) + return false; + + local_data().local_symbol_list_.push_back(value); + T& t = local_data().local_symbol_list_.back(); + + return add_variable(constant_name, t, true); + } + + #ifndef exprtk_disable_string_capabilities + inline bool add_stringvar(const std::string& stringvar_name, std::string& s, const bool is_constant = false) + { + if (!valid()) + return false; + else if (!valid_symbol(stringvar_name)) + return false; + else if (symbol_exists(stringvar_name)) + return false; + else + return local_data().stringvar_store.add(stringvar_name, s, is_constant); + } + #endif + + inline bool add_function(const std::string& function_name, function_t& function) + { + if (!valid()) + return false; + else if (!valid_symbol(function_name)) + return false; + else if (symbol_exists(function_name)) + return false; + else + return local_data().function_store.add(function_name,function); + } + + inline bool add_function(const std::string& vararg_function_name, vararg_function_t& vararg_function) + { + if (!valid()) + return false; + else if (!valid_symbol(vararg_function_name)) + return false; + else if (symbol_exists(vararg_function_name)) + return false; + else + return local_data().vararg_function_store.add(vararg_function_name,vararg_function); + } + + inline bool add_function(const std::string& function_name, generic_function_t& function) + { + if (!valid()) + return false; + else if (!valid_symbol(function_name)) + return false; + else if (symbol_exists(function_name)) + return false; + else + { + switch (function.rtrn_type) + { + case generic_function_t::e_rtrn_scalar : + return (std::string::npos == function.parameter_sequence.find_first_not_of("STVZ*?|")) ? + local_data().generic_function_store.add(function_name,function) : false; + + case generic_function_t::e_rtrn_string : + return (std::string::npos == function.parameter_sequence.find_first_not_of("STVZ*?|")) ? + local_data().string_function_store.add(function_name,function) : false; + + case generic_function_t::e_rtrn_overload : + return (std::string::npos == function.parameter_sequence.find_first_not_of("STVZ*?|:")) ? + local_data().overload_function_store.add(function_name,function) : false; + } + } + + return false; + } + + #define exprtk_define_freefunction(NN) \ + inline bool add_function(const std::string& function_name, ff##NN##_functor function) \ + { \ + if (!valid()) \ + { return false; } \ + if (!valid_symbol(function_name)) \ + { return false; } \ + if (symbol_exists(function_name)) \ + { return false; } \ + \ + exprtk::ifunction* ifunc = new freefunc##NN(function); \ + \ + local_data().free_function_list_.push_back(ifunc); \ + \ + return add_function(function_name,(*local_data().free_function_list_.back())); \ + } \ + + exprtk_define_freefunction(00) exprtk_define_freefunction(01) + exprtk_define_freefunction(02) exprtk_define_freefunction(03) + exprtk_define_freefunction(04) exprtk_define_freefunction(05) + exprtk_define_freefunction(06) exprtk_define_freefunction(07) + exprtk_define_freefunction(08) exprtk_define_freefunction(09) + exprtk_define_freefunction(10) exprtk_define_freefunction(11) + exprtk_define_freefunction(12) exprtk_define_freefunction(13) + exprtk_define_freefunction(14) exprtk_define_freefunction(15) + + #undef exprtk_define_freefunction + + inline bool add_reserved_function(const std::string& function_name, function_t& function) + { + if (!valid()) + return false; + else if (!valid_symbol(function_name,false)) + return false; + else if (symbol_exists(function_name,false)) + return false; + else + return local_data().function_store.add(function_name,function); + } + + inline bool add_reserved_function(const std::string& vararg_function_name, vararg_function_t& vararg_function) + { + if (!valid()) + return false; + else if (!valid_symbol(vararg_function_name,false)) + return false; + else if (symbol_exists(vararg_function_name,false)) + return false; + else + return local_data().vararg_function_store.add(vararg_function_name,vararg_function); + } + + inline bool add_reserved_function(const std::string& function_name, generic_function_t& function) + { + if (!valid()) + return false; + else if (!valid_symbol(function_name,false)) + return false; + else if (symbol_exists(function_name,false)) + return false; + else + { + switch (function.rtrn_type) + { + case generic_function_t::e_rtrn_scalar : + return (std::string::npos == function.parameter_sequence.find_first_not_of("STVZ*?|")) ? + local_data().generic_function_store.add(function_name,function) : false; + + case generic_function_t::e_rtrn_string : + return (std::string::npos == function.parameter_sequence.find_first_not_of("STVZ*?|")) ? + local_data().string_function_store.add(function_name,function) : false; + + case generic_function_t::e_rtrn_overload : + return (std::string::npos == function.parameter_sequence.find_first_not_of("STVZ*?|:")) ? + local_data().overload_function_store.add(function_name,function) : false; + } + } + + return false; + } + + #define exprtk_define_reserved_function(NN) \ + inline bool add_reserved_function(const std::string& function_name, ff##NN##_functor function) \ + { \ + if (!valid()) \ + { return false; } \ + if (!valid_symbol(function_name,false)) \ + { return false; } \ + if (symbol_exists(function_name,false)) \ + { return false; } \ + \ + exprtk::ifunction* ifunc = new freefunc##NN(function); \ + \ + local_data().free_function_list_.push_back(ifunc); \ + \ + return add_reserved_function(function_name,(*local_data().free_function_list_.back())); \ + } \ + + exprtk_define_reserved_function(00) exprtk_define_reserved_function(01) + exprtk_define_reserved_function(02) exprtk_define_reserved_function(03) + exprtk_define_reserved_function(04) exprtk_define_reserved_function(05) + exprtk_define_reserved_function(06) exprtk_define_reserved_function(07) + exprtk_define_reserved_function(08) exprtk_define_reserved_function(09) + exprtk_define_reserved_function(10) exprtk_define_reserved_function(11) + exprtk_define_reserved_function(12) exprtk_define_reserved_function(13) + exprtk_define_reserved_function(14) exprtk_define_reserved_function(15) + + #undef exprtk_define_reserved_function + + template + inline bool add_vector(const std::string& vector_name, T (&v)[N]) + { + if (!valid()) + return false; + else if (!valid_symbol(vector_name)) + return false; + else if (symbol_exists(vector_name)) + return false; + else + return local_data().vector_store.add(vector_name,v); + } + + inline bool add_vector(const std::string& vector_name, T* v, const std::size_t& v_size) + { + if (!valid()) + return false; + else if (!valid_symbol(vector_name)) + return false; + else if (symbol_exists(vector_name)) + return false; + else if (0 == v_size) + return false; + else + return local_data().vector_store.add(vector_name, v, v_size); + } + + template + inline bool add_vector(const std::string& vector_name, std::vector& v) + { + if (!valid()) + return false; + else if (!valid_symbol(vector_name)) + return false; + else if (symbol_exists(vector_name)) + return false; + else if (0 == v.size()) + return false; + else + return local_data().vector_store.add(vector_name,v); + } + + inline bool add_vector(const std::string& vector_name, exprtk::vector_view& v) + { + if (!valid()) + return false; + else if (!valid_symbol(vector_name)) + return false; + else if (symbol_exists(vector_name)) + return false; + else if (0 == v.size()) + return false; + else + return local_data().vector_store.add(vector_name,v); + } + + inline bool remove_variable(const std::string& variable_name, const bool delete_node = true) + { + if (!valid()) + return false; + else + return local_data().variable_store.remove(variable_name, delete_node); + } + + #ifndef exprtk_disable_string_capabilities + inline bool remove_stringvar(const std::string& string_name) + { + if (!valid()) + return false; + else + return local_data().stringvar_store.remove(string_name); + } + #endif + + inline bool remove_function(const std::string& function_name) + { + if (!valid()) + return false; + else + return local_data().function_store.remove(function_name); + } + + inline bool remove_vararg_function(const std::string& vararg_function_name) + { + if (!valid()) + return false; + else + return local_data().vararg_function_store.remove(vararg_function_name); + } + + inline bool remove_vector(const std::string& vector_name) + { + if (!valid()) + return false; + else + return local_data().vector_store.remove(vector_name); + } + + inline bool add_constants() + { + return add_pi () && + add_epsilon () && + add_infinity() ; + } + + inline bool add_pi() + { + const typename details::numeric::details::number_type::type num_type; + static const T local_pi = details::numeric::details::const_pi_impl(num_type); + return add_constant("pi",local_pi); + } + + inline bool add_epsilon() + { + static const T local_epsilon = details::numeric::details::epsilon_type::value(); + return add_constant("epsilon",local_epsilon); + } + + inline bool add_infinity() + { + static const T local_infinity = std::numeric_limits::infinity(); + return add_constant("inf",local_infinity); + } + + template + inline bool add_package(Package& package) + { + return package.register_package(*this); + } + + template class Sequence> + inline std::size_t get_variable_list(Sequence,Allocator>& vlist) const + { + if (!valid()) + return 0; + else + return local_data().variable_store.get_list(vlist); + } + + template class Sequence> + inline std::size_t get_variable_list(Sequence& vlist) const + { + if (!valid()) + return 0; + else + return local_data().variable_store.get_list(vlist); + } + + #ifndef exprtk_disable_string_capabilities + template class Sequence> + inline std::size_t get_stringvar_list(Sequence,Allocator>& svlist) const + { + if (!valid()) + return 0; + else + return local_data().stringvar_store.get_list(svlist); + } + + template class Sequence> + inline std::size_t get_stringvar_list(Sequence& svlist) const + { + if (!valid()) + return 0; + else + return local_data().stringvar_store.get_list(svlist); + } + #endif + + template class Sequence> + inline std::size_t get_vector_list(Sequence& vec_list) const + { + if (!valid()) + return 0; + else + return local_data().vector_store.get_list(vec_list); + } + + template class Sequence> + inline std::size_t get_function_list(Sequence& function_list) const + { + if (!valid()) + return 0; + + std::vector function_names; + std::size_t count = 0; + + count += local_data().function_store .get_list(function_names); + count += local_data().vararg_function_store .get_list(function_names); + count += local_data().generic_function_store .get_list(function_names); + count += local_data().string_function_store .get_list(function_names); + count += local_data().overload_function_store.get_list(function_names); + + std::set function_set; + + for (std::size_t i = 0; i < function_names.size(); ++i) + { + function_set.insert(function_names[i]); + } + + std::copy(function_set.begin(), function_set.end(), + std::back_inserter(function_list)); + + return count; + } + + inline std::vector get_function_list() const + { + std::vector result; + get_function_list(result); + return result; + } + + inline bool symbol_exists(const std::string& symbol_name, const bool check_reserved_symb = true) const + { + /* + Function will return true if symbol_name exists as either a + reserved symbol, variable, stringvar, vector or function name + in any of the type stores. + */ + if (!valid()) + return false; + else if (local_data().variable_store.symbol_exists(symbol_name)) + return true; + #ifndef exprtk_disable_string_capabilities + else if (local_data().stringvar_store.symbol_exists(symbol_name)) + return true; + #endif + else if (local_data().vector_store.symbol_exists(symbol_name)) + return true; + else if (local_data().function_store.symbol_exists(symbol_name)) + return true; + else if (check_reserved_symb && local_data().is_reserved_symbol(symbol_name)) + return true; + else + return false; + } + + inline bool is_variable(const std::string& variable_name) const + { + if (!valid()) + return false; + else + return local_data().variable_store.symbol_exists(variable_name); + } + + #ifndef exprtk_disable_string_capabilities + inline bool is_stringvar(const std::string& stringvar_name) const + { + if (!valid()) + return false; + else + return local_data().stringvar_store.symbol_exists(stringvar_name); + } + + inline bool is_conststr_stringvar(const std::string& symbol_name) const + { + if (!valid()) + return false; + else if (!valid_symbol(symbol_name)) + return false; + else if (!local_data().stringvar_store.symbol_exists(symbol_name)) + return false; + + return ( + local_data().stringvar_store.symbol_exists(symbol_name) || + local_data().stringvar_store.is_constant (symbol_name) + ); + } + #endif + + inline bool is_function(const std::string& function_name) const + { + if (!valid()) + return false; + else + return local_data().function_store.symbol_exists(function_name); + } + + inline bool is_vararg_function(const std::string& vararg_function_name) const + { + if (!valid()) + return false; + else + return local_data().vararg_function_store.symbol_exists(vararg_function_name); + } + + inline bool is_vector(const std::string& vector_name) const + { + if (!valid()) + return false; + else + return local_data().vector_store.symbol_exists(vector_name); + } + + inline std::string get_variable_name(const expression_ptr& ptr) const + { + return local_data().variable_store.entity_name(ptr); + } + + inline std::string get_vector_name(const vector_holder_ptr& ptr) const + { + return local_data().vector_store.entity_name(ptr); + } + + #ifndef exprtk_disable_string_capabilities + inline std::string get_stringvar_name(const expression_ptr& ptr) const + { + return local_data().stringvar_store.entity_name(ptr); + } + + inline std::string get_conststr_stringvar_name(const expression_ptr& ptr) const + { + return local_data().stringvar_store.entity_name(ptr); + } + #endif + + inline bool valid() const + { + // Symbol table sanity check. + return control_block_ && control_block_->data_; + } + + inline void load_from(const symbol_table& st) + { + { + std::vector name_list; + + st.local_data().function_store.get_list(name_list); + + if (!name_list.empty()) + { + for (std::size_t i = 0; i < name_list.size(); ++i) + { + exprtk::ifunction& ifunc = *st.get_function(name_list[i]); + add_function(name_list[i],ifunc); + } + } + } + + { + std::vector name_list; + + st.local_data().vararg_function_store.get_list(name_list); + + if (!name_list.empty()) + { + for (std::size_t i = 0; i < name_list.size(); ++i) + { + exprtk::ivararg_function& ivafunc = *st.get_vararg_function(name_list[i]); + add_function(name_list[i],ivafunc); + } + } + } + + { + std::vector name_list; + + st.local_data().generic_function_store.get_list(name_list); + + if (!name_list.empty()) + { + for (std::size_t i = 0; i < name_list.size(); ++i) + { + exprtk::igeneric_function& ifunc = *st.get_generic_function(name_list[i]); + add_function(name_list[i],ifunc); + } + } + } + + { + std::vector name_list; + + st.local_data().string_function_store.get_list(name_list); + + if (!name_list.empty()) + { + for (std::size_t i = 0; i < name_list.size(); ++i) + { + exprtk::igeneric_function& ifunc = *st.get_string_function(name_list[i]); + add_function(name_list[i],ifunc); + } + } + } + + { + std::vector name_list; + + st.local_data().overload_function_store.get_list(name_list); + + if (!name_list.empty()) + { + for (std::size_t i = 0; i < name_list.size(); ++i) + { + exprtk::igeneric_function& ifunc = *st.get_overload_function(name_list[i]); + add_function(name_list[i],ifunc); + } + } + } + } + + inline void load_variables_from(const symbol_table& st) + { + std::vector name_list; + + st.local_data().variable_store.get_list(name_list); + + if (!name_list.empty()) + { + for (std::size_t i = 0; i < name_list.size(); ++i) + { + T& variable = st.get_variable(name_list[i])->ref(); + add_variable(name_list[i], variable); + } + } + } + + inline void load_vectors_from(const symbol_table& st) + { + std::vector name_list; + + st.local_data().vector_store.get_list(name_list); + + if (!name_list.empty()) + { + for (std::size_t i = 0; i < name_list.size(); ++i) + { + vector_holder_t& vecholder = *st.get_vector(name_list[i]); + add_vector(name_list[i], vecholder.data(), vecholder.size()); + } + } + } + + private: + + inline bool valid_symbol(const std::string& symbol, const bool check_reserved_symb = true) const + { + if (symbol.empty()) + return false; + else if (!details::is_letter(symbol[0])) + return false; + else if (symbol.size() > 1) + { + for (std::size_t i = 1; i < symbol.size(); ++i) + { + if ( + !details::is_letter_or_digit(symbol[i]) && + ('_' != symbol[i]) + ) + { + if ((i < (symbol.size() - 1)) && ('.' == symbol[i])) + continue; + else + return false; + } + } + } + + return (check_reserved_symb) ? (!local_data().is_reserved_symbol(symbol)) : true; + } + + inline bool valid_function(const std::string& symbol) const + { + if (symbol.empty()) + return false; + else if (!details::is_letter(symbol[0])) + return false; + else if (symbol.size() > 1) + { + for (std::size_t i = 1; i < symbol.size(); ++i) + { + if ( + !details::is_letter_or_digit(symbol[i]) && + ('_' != symbol[i]) + ) + { + if ((i < (symbol.size() - 1)) && ('.' == symbol[i])) + continue; + else + return false; + } + } + } + + return true; + } + + typedef typename control_block::st_data local_data_t; + + inline local_data_t& local_data() + { + return *(control_block_->data_); + } + + inline const local_data_t& local_data() const + { + return *(control_block_->data_); + } + + control_block* control_block_; + + friend class parser; + }; // class symbol_table + + template + class function_compositor; + + template + class expression + { + private: + + typedef details::expression_node* expression_ptr; + typedef details::vector_holder* vector_holder_ptr; + typedef std::vector > symtab_list_t; + + struct control_block + { + enum data_type + { + e_unknown , + e_expr , + e_vecholder, + e_data , + e_vecdata , + e_string + }; + + static std::string to_str(data_type dt) + { + switch (dt) + { + case e_unknown : return "e_unknown "; + case e_expr : return "e_expr" ; + case e_vecholder : return "e_vecholder"; + case e_data : return "e_data" ; + case e_vecdata : return "e_vecdata" ; + case e_string : return "e_string" ; + } + + return ""; + } + + struct data_pack + { + data_pack() + : pointer(0) + , type(e_unknown) + , size(0) + {} + + data_pack(void* ptr, const data_type dt, const std::size_t sz = 0) + : pointer(ptr) + , type(dt) + , size(sz) + {} + + void* pointer; + data_type type; + std::size_t size; + }; + + typedef std::vector local_data_list_t; + typedef results_context results_context_t; + typedef control_block* cntrl_blck_ptr_t; + + control_block() + : ref_count(0) + , expr (0) + , results (0) + , retinv_null(false) + , return_invoked(&retinv_null) + {} + + explicit control_block(expression_ptr e) + : ref_count(1) + , expr (e) + , results (0) + , retinv_null(false) + , return_invoked(&retinv_null) + {} + + ~control_block() + { + if (expr && details::branch_deletable(expr)) + { + destroy_node(expr); + } + + if (!local_data_list.empty()) + { + for (std::size_t i = 0; i < local_data_list.size(); ++i) + { + switch (local_data_list[i].type) + { + case e_expr : delete reinterpret_cast(local_data_list[i].pointer); + break; + + case e_vecholder : delete reinterpret_cast(local_data_list[i].pointer); + break; + + case e_data : delete reinterpret_cast(local_data_list[i].pointer); + break; + + case e_vecdata : delete [] reinterpret_cast(local_data_list[i].pointer); + break; + + case e_string : delete reinterpret_cast(local_data_list[i].pointer); + break; + + default : break; + } + } + } + + if (results) + { + delete results; + } + } + + static inline cntrl_blck_ptr_t create(expression_ptr e) + { + return new control_block(e); + } + + static inline void destroy(cntrl_blck_ptr_t& cntrl_blck) + { + if (cntrl_blck) + { + if ( + (0 != cntrl_blck->ref_count) && + (0 == --cntrl_blck->ref_count) + ) + { + delete cntrl_blck; + } + + cntrl_blck = 0; + } + } + + std::size_t ref_count; + expression_ptr expr; + local_data_list_t local_data_list; + results_context_t* results; + bool retinv_null; + bool* return_invoked; + + friend class function_compositor; + }; + + public: + + expression() + : control_block_(0) + { + set_expression(new details::null_node()); + } + + expression(const expression& e) + : control_block_ (e.control_block_ ) + , symbol_table_list_(e.symbol_table_list_) + { + control_block_->ref_count++; + } + + explicit expression(const symbol_table& symbol_table) + : control_block_(0) + { + set_expression(new details::null_node()); + symbol_table_list_.push_back(symbol_table); + } + + inline expression& operator=(const expression& e) + { + if (this != &e) + { + if (control_block_) + { + if ( + (0 != control_block_->ref_count) && + (0 == --control_block_->ref_count) + ) + { + delete control_block_; + } + + control_block_ = 0; + } + + control_block_ = e.control_block_; + control_block_->ref_count++; + symbol_table_list_ = e.symbol_table_list_; + } + + return *this; + } + + inline bool operator==(const expression& e) const + { + return (this == &e); + } + + inline bool operator!() const + { + return ( + (0 == control_block_ ) || + (0 == control_block_->expr) + ); + } + + inline expression& release() + { + exprtk::details::dump_ptr("expression::release", this); + control_block::destroy(control_block_); + + return (*this); + } + + ~expression() + { + control_block::destroy(control_block_); + } + + inline T value() const + { + assert(control_block_ ); + assert(control_block_->expr); + + return control_block_->expr->value(); + } + + inline T operator() () const + { + return value(); + } + + inline operator T() const + { + return value(); + } + + inline operator bool() const + { + return details::is_true(value()); + } + + inline bool register_symbol_table(symbol_table& st) + { + for (std::size_t i = 0; i < symbol_table_list_.size(); ++i) + { + if (st == symbol_table_list_[i]) + { + return false; + } + } + + symbol_table_list_.push_back(st); + return true; + } + + inline const symbol_table& get_symbol_table(const std::size_t& index = 0) const + { + return symbol_table_list_[index]; + } + + inline symbol_table& get_symbol_table(const std::size_t& index = 0) + { + return symbol_table_list_[index]; + } + + std::size_t num_symbol_tables() const + { + return symbol_table_list_.size(); + } + + typedef results_context results_context_t; + + inline const results_context_t& results() const + { + if (control_block_->results) + return (*control_block_->results); + else + { + static const results_context_t null_results; + return null_results; + } + } + + inline bool return_invoked() const + { + return (*control_block_->return_invoked); + } + + private: + + inline symtab_list_t get_symbol_table_list() const + { + return symbol_table_list_; + } + + inline void set_expression(const expression_ptr expr) + { + if (expr) + { + if (control_block_) + { + if (0 == --control_block_->ref_count) + { + delete control_block_; + } + } + + control_block_ = control_block::create(expr); + } + } + + inline void register_local_var(expression_ptr expr) + { + if (expr) + { + if (control_block_) + { + control_block_-> + local_data_list.push_back( + typename expression::control_block:: + data_pack(reinterpret_cast(expr), + control_block::e_expr)); + } + } + } + + inline void register_local_var(vector_holder_ptr vec_holder) + { + if (vec_holder) + { + if (control_block_) + { + control_block_-> + local_data_list.push_back( + typename expression::control_block:: + data_pack(reinterpret_cast(vec_holder), + control_block::e_vecholder)); + } + } + } + + inline void register_local_data(void* data, const std::size_t& size = 0, const std::size_t data_mode = 0) + { + if (data) + { + if (control_block_) + { + typename control_block::data_type dt = control_block::e_data; + + switch (data_mode) + { + case 0 : dt = control_block::e_data; break; + case 1 : dt = control_block::e_vecdata; break; + case 2 : dt = control_block::e_string; break; + } + + control_block_-> + local_data_list.push_back( + typename expression::control_block:: + data_pack(reinterpret_cast(data), dt, size)); + } + } + } + + inline const typename control_block::local_data_list_t& local_data_list() + { + if (control_block_) + { + return control_block_->local_data_list; + } + else + { + static typename control_block::local_data_list_t null_local_data_list; + return null_local_data_list; + } + } + + inline void register_return_results(results_context_t* rc) + { + if (control_block_ && rc) + { + control_block_->results = rc; + } + } + + inline void set_retinvk(bool* retinvk_ptr) + { + if (control_block_) + { + control_block_->return_invoked = retinvk_ptr; + } + } + + control_block* control_block_; + symtab_list_t symbol_table_list_; + + friend class parser; + friend class expression_helper; + friend class function_compositor; + template + friend bool is_valid(const expression& expr); + }; // class expression + + template + class expression_helper + { + public: + + enum node_types + { + e_literal, + e_variable, + e_string, + e_unary, + e_binary, + e_function, + e_vararg, + e_null, + e_assert, + e_sf3ext, + e_sf4ext + }; + + static inline bool is_literal(const expression& expr) + { + return expr.control_block_ && details::is_literal_node(expr.control_block_->expr); + } + + static inline bool is_variable(const expression& expr) + { + return expr.control_block_ && details::is_variable_node(expr.control_block_->expr); + } + + static inline bool is_string(const expression& expr) + { + return expr.control_block_ && details::is_generally_string_node(expr.control_block_->expr); + } + + static inline bool is_unary(const expression& expr) + { + return expr.control_block_ && details::is_unary_node(expr.control_block_->expr); + } + + static inline bool is_binary(const expression& expr) + { + return expr.control_block_ && details::is_binary_node(expr.control_block_->expr); + } + + static inline bool is_function(const expression& expr) + { + return expr.control_block_ && details::is_function(expr.control_block_->expr); + } + + static inline bool is_vararg(const expression& expr) + { + return expr.control_block_ && details::is_vararg_node(expr.control_block_->expr); + } + + static inline bool is_null(const expression& expr) + { + return expr.control_block_ && details::is_null_node(expr.control_block_->expr); + } + + static inline bool is_assert(const expression& expr) + { + return expr.control_block_ && details::is_assert_node(expr.control_block_->expr); + } + + static inline bool is_sf3ext(const expression& expr) + { + return expr.control_block_ && details::is_sf3ext_node(expr.control_block_->expr); + } + + static inline bool is_sf4ext(const expression& expr) + { + return expr.control_block_ && details::is_sf4ext_node(expr.control_block_->expr); + } + + static inline bool is_type(const expression& expr, const node_types node_type) + { + if (0 == expr.control_block_) + { + return false; + } + + switch (node_type) + { + case e_literal : return is_literal_node(expr); + case e_variable : return is_variable (expr); + case e_string : return is_string (expr); + case e_unary : return is_unary (expr); + case e_binary : return is_binary (expr); + case e_function : return is_function (expr); + case e_null : return is_null (expr); + case e_assert : return is_assert (expr); + case e_sf3ext : return is_sf3ext (expr); + case e_sf4ext : return is_sf4ext (expr); + }; + + return false; + } + + static inline bool match_type_sequence(const expression& expr, const std::vector& type_seq) + { + if ((0 == expr.control_block_) || !is_vararg(expr)) + { + return false; + } + + typedef details::vararg_node > mo_vararg_t; + + mo_vararg_t* vnode = dynamic_cast(expr.control_block_->expr); + + if ( + (0 == vnode) || + type_seq.empty() || + (vnode->size() < type_seq.size()) + ) + { + return false; + } + + for (std::size_t i = 0; i < type_seq.size(); ++i) + { + assert((*vnode)[i]); + + switch (type_seq[i]) + { + case e_literal : { if (details::is_literal_node ((*vnode)[i])) continue; } break; + case e_variable : { if (details::is_variable_node ((*vnode)[i])) continue; } break; + case e_string : { if (details::is_generally_string_node((*vnode)[i])) continue; } break; + case e_unary : { if (details::is_unary_node ((*vnode)[i])) continue; } break; + case e_binary : { if (details::is_binary_node ((*vnode)[i])) continue; } break; + case e_function : { if (details::is_function ((*vnode)[i])) continue; } break; + case e_null : { if (details::is_null_node ((*vnode)[i])) continue; } break; + case e_assert : { if (details::is_assert_node ((*vnode)[i])) continue; } break; + case e_sf3ext : { if (details::is_sf3ext_node ((*vnode)[i])) continue; } break; + case e_sf4ext : { if (details::is_sf4ext_node ((*vnode)[i])) continue; } break; + case e_vararg : break; + } + + return false; + } + + return true; + } + }; + + template + inline bool is_valid(const expression& expr) + { + return expr.control_block_ && !expression_helper::is_null(expr); + } + + namespace parser_error + { + enum error_mode + { + e_unknown = 0, + e_syntax = 1, + e_token = 2, + e_numeric = 4, + e_symtab = 5, + e_lexer = 6, + e_synthesis = 7, + e_helper = 8, + e_parser = 9 + }; + + struct type + { + type() + : mode(parser_error::e_unknown) + , line_no (0) + , column_no(0) + {} + + lexer::token token; + error_mode mode; + std::string diagnostic; + std::string src_location; + std::string error_line; + std::size_t line_no; + std::size_t column_no; + }; + + inline type make_error(const error_mode mode, + const std::string& diagnostic = "", + const std::string& src_location = "") + { + type t; + t.mode = mode; + t.token.type = lexer::token::e_error; + t.diagnostic = diagnostic; + t.src_location = src_location; + exprtk_debug(("%s\n", diagnostic .c_str())); + return t; + } + + inline type make_error(const error_mode mode, + const lexer::token& tk, + const std::string& diagnostic = "", + const std::string& src_location = "") + { + type t; + t.mode = mode; + t.token = tk; + t.diagnostic = diagnostic; + t.src_location = src_location; + exprtk_debug(("%s\n", diagnostic .c_str())); + return t; + } + + inline std::string to_str(error_mode mode) + { + switch (mode) + { + case e_unknown : return std::string("Unknown Error"); + case e_syntax : return std::string("Syntax Error" ); + case e_token : return std::string("Token Error" ); + case e_numeric : return std::string("Numeric Error"); + case e_symtab : return std::string("Symbol Error" ); + case e_lexer : return std::string("Lexer Error" ); + case e_helper : return std::string("Helper Error" ); + case e_parser : return std::string("Parser Error" ); + default : return std::string("Unknown Error"); + } + } + + inline bool update_error(type& error, const std::string& expression) + { + if ( + expression.empty() || + (error.token.position > expression.size()) || + (std::numeric_limits::max() == error.token.position) + ) + { + return false; + } + + std::size_t error_line_start = 0; + + for (std::size_t i = error.token.position; i > 0; --i) + { + const details::char_t c = expression[i]; + + if (('\n' == c) || ('\r' == c)) + { + error_line_start = i + 1; + break; + } + } + + std::size_t next_nl_position = std::min(expression.size(), + expression.find_first_of('\n',error.token.position + 1)); + + error.column_no = error.token.position - error_line_start; + error.error_line = expression.substr(error_line_start, + next_nl_position - error_line_start); + + error.line_no = 0; + + for (std::size_t i = 0; i < next_nl_position; ++i) + { + if ('\n' == expression[i]) + ++error.line_no; + } + + return true; + } + + inline void dump_error(const type& error) + { + printf("Position: %02d Type: [%s] Msg: %s\n", + static_cast(error.token.position), + exprtk::parser_error::to_str(error.mode).c_str(), + error.diagnostic.c_str()); + } + } + + namespace details + { + template + inline void disable_type_checking(Parser& p) + { + p.state_.type_check_enabled = false; + } + } + + template + class parser : public lexer::parser_helper + { + private: + + enum precedence_level + { + e_level00, e_level01, e_level02, e_level03, e_level04, + e_level05, e_level06, e_level07, e_level08, e_level09, + e_level10, e_level11, e_level12, e_level13, e_level14 + }; + + typedef const T& cref_t; + typedef const T const_t; + typedef ifunction F; + typedef ivararg_function VAF; + typedef igeneric_function GF; + typedef ifunction ifunction_t; + typedef ivararg_function ivararg_function_t; + typedef igeneric_function igeneric_function_t; + typedef details::expression_node expression_node_t; + typedef details::literal_node literal_node_t; + typedef details::unary_node unary_node_t; + typedef details::binary_node binary_node_t; + typedef details::trinary_node trinary_node_t; + typedef details::quaternary_node quaternary_node_t; + typedef details::conditional_node conditional_node_t; + typedef details::cons_conditional_node cons_conditional_node_t; + typedef details::while_loop_node while_loop_node_t; + typedef details::repeat_until_loop_node repeat_until_loop_node_t; + typedef details::for_loop_node for_loop_node_t; + typedef details::while_loop_rtc_node while_loop_rtc_node_t; + typedef details::repeat_until_loop_rtc_node repeat_until_loop_rtc_node_t; + typedef details::for_loop_rtc_node for_loop_rtc_node_t; + #ifndef exprtk_disable_break_continue + typedef details::while_loop_bc_node while_loop_bc_node_t; + typedef details::repeat_until_loop_bc_node repeat_until_loop_bc_node_t; + typedef details::for_loop_bc_node for_loop_bc_node_t; + typedef details::while_loop_bc_rtc_node while_loop_bc_rtc_node_t; + typedef details::repeat_until_loop_bc_rtc_node repeat_until_loop_bc_rtc_node_t; + typedef details::for_loop_bc_rtc_node for_loop_bc_rtc_node_t; + #endif + typedef details::switch_node switch_node_t; + typedef details::variable_node variable_node_t; + typedef details::vector_elem_node vector_elem_node_t; + typedef details::vector_celem_node vector_celem_node_t; + typedef details::vector_elem_rtc_node vector_elem_rtc_node_t; + typedef details::vector_celem_rtc_node vector_celem_rtc_node_t; + typedef details::rebasevector_elem_node rebasevector_elem_node_t; + typedef details::rebasevector_celem_node rebasevector_celem_node_t; + typedef details::rebasevector_elem_rtc_node rebasevector_elem_rtc_node_t; + typedef details::rebasevector_celem_rtc_node rebasevector_celem_rtc_node_t; + typedef details::vector_node vector_node_t; + typedef details::vector_size_node vector_size_node_t; + typedef details::range_pack range_t; + #ifndef exprtk_disable_string_capabilities + typedef details::stringvar_node stringvar_node_t; + typedef details::string_literal_node string_literal_node_t; + typedef details::string_range_node string_range_node_t; + typedef details::const_string_range_node const_string_range_node_t; + typedef details::generic_string_range_node generic_string_range_node_t; + typedef details::string_concat_node string_concat_node_t; + typedef details::assignment_string_node assignment_string_node_t; + typedef details::assignment_string_range_node assignment_string_range_node_t; + typedef details::conditional_string_node conditional_string_node_t; + typedef details::cons_conditional_str_node cons_conditional_str_node_t; + #endif + typedef details::assignment_node assignment_node_t; + typedef details::assignment_vec_elem_node assignment_vec_elem_node_t; + typedef details::assignment_vec_elem_rtc_node assignment_vec_elem_rtc_node_t; + typedef details::assignment_rebasevec_elem_node assignment_rebasevec_elem_node_t; + typedef details::assignment_rebasevec_elem_rtc_node assignment_rebasevec_elem_rtc_node_t; + typedef details::assignment_rebasevec_celem_node assignment_rebasevec_celem_node_t; + typedef details::assignment_vec_node assignment_vec_node_t; + typedef details::assignment_vecvec_node assignment_vecvec_node_t; + typedef details::conditional_vector_node conditional_vector_node_t; + typedef details::scand_node scand_node_t; + typedef details::scor_node scor_node_t; + typedef lexer::token token_t; + typedef expression_node_t* expression_node_ptr; + typedef expression expression_t; + typedef symbol_table symbol_table_t; + typedef typename expression::symtab_list_t symbol_table_list_t; + typedef details::vector_holder vector_holder_t; + typedef vector_holder_t* vector_holder_ptr; + + typedef typename details::functor_t functor_t; + typedef typename functor_t::qfunc_t quaternary_functor_t; + typedef typename functor_t::tfunc_t trinary_functor_t; + typedef typename functor_t::bfunc_t binary_functor_t; + typedef typename functor_t::ufunc_t unary_functor_t; + + typedef details::operator_type operator_t; + + typedef std::map unary_op_map_t; + typedef std::map binary_op_map_t; + typedef std::map trinary_op_map_t; + + typedef std::map > sf3_map_t; + typedef std::map > sf4_map_t; + + typedef std::map inv_binary_op_map_t; + typedef std::multimap base_ops_map_t; + typedef std::set disabled_func_set_t; + + typedef details::T0oT1_define vov_t; + typedef details::T0oT1_define cov_t; + typedef details::T0oT1_define voc_t; + + typedef details::T0oT1oT2_define vovov_t; + typedef details::T0oT1oT2_define vovoc_t; + typedef details::T0oT1oT2_define vocov_t; + typedef details::T0oT1oT2_define covov_t; + typedef details::T0oT1oT2_define covoc_t; + typedef details::T0oT1oT2_define cocov_t; + typedef details::T0oT1oT2_define vococ_t; + + typedef details::T0oT1oT2oT3_define vovovov_t; + typedef details::T0oT1oT2oT3_define vovovoc_t; + typedef details::T0oT1oT2oT3_define vovocov_t; + typedef details::T0oT1oT2oT3_define vocovov_t; + typedef details::T0oT1oT2oT3_define covovov_t; + + typedef details::T0oT1oT2oT3_define covocov_t; + typedef details::T0oT1oT2oT3_define vocovoc_t; + typedef details::T0oT1oT2oT3_define covovoc_t; + typedef details::T0oT1oT2oT3_define vococov_t; + + typedef results_context results_context_t; + + typedef parser_helper prsrhlpr_t; + + struct scope_element + { + enum element_type + { + e_none , + e_literal , + e_variable, + e_vector , + e_vecelem , + e_string + }; + + typedef details::vector_holder vector_holder_t; + typedef literal_node_t* literal_node_ptr; + typedef variable_node_t* variable_node_ptr; + typedef vector_holder_t* vector_holder_ptr; + typedef expression_node_t* expression_node_ptr; + #ifndef exprtk_disable_string_capabilities + typedef stringvar_node_t* stringvar_node_ptr; + #endif + + scope_element() + : name("???") + , size (std::numeric_limits::max()) + , index(std::numeric_limits::max()) + , depth(std::numeric_limits::max()) + , ref_count(0) + , ip_index (0) + , type (e_none) + , active (false) + , data (0) + , var_node (0) + , vec_node (0) + #ifndef exprtk_disable_string_capabilities + , str_node(0) + #endif + {} + + bool operator < (const scope_element& se) const + { + if (ip_index < se.ip_index) + return true; + else if (ip_index > se.ip_index) + return false; + else if (depth < se.depth) + return true; + else if (depth > se.depth) + return false; + else if (index < se.index) + return true; + else if (index > se.index) + return false; + else + return (name < se.name); + } + + void clear() + { + name = "???"; + size = std::numeric_limits::max(); + index = std::numeric_limits::max(); + depth = std::numeric_limits::max(); + type = e_none; + active = false; + ref_count = 0; + ip_index = 0; + data = 0; + var_node = 0; + vec_node = 0; + #ifndef exprtk_disable_string_capabilities + str_node = 0; + #endif + } + + std::string name; + std::size_t size; + std::size_t index; + std::size_t depth; + std::size_t ref_count; + std::size_t ip_index; + element_type type; + bool active; + void* data; + expression_node_ptr var_node; + vector_holder_ptr vec_node; + #ifndef exprtk_disable_string_capabilities + stringvar_node_ptr str_node; + #endif + }; + + class scope_element_manager + { + public: + + typedef expression_node_t* expression_node_ptr; + typedef variable_node_t* variable_node_ptr; + typedef parser parser_t; + + explicit scope_element_manager(parser& p) + : parser_(p) + , input_param_cnt_(0) + , total_local_symb_size_bytes_(0) + {} + + inline std::size_t size() const + { + return element_.size(); + } + + inline bool empty() const + { + return element_.empty(); + } + + inline scope_element& get_element(const std::size_t& index) + { + if (index < element_.size()) + return element_[index]; + else + return null_element_; + } + + inline scope_element& get_element(const std::string& var_name, + const std::size_t index = std::numeric_limits::max()) + { + const std::size_t current_depth = parser_.state_.scope_depth; + + for (std::size_t i = 0; i < element_.size(); ++i) + { + scope_element& se = element_[i]; + + if (se.depth > current_depth) + continue; + else if ( + details::imatch(se.name, var_name) && + (se.index == index) + ) + return se; + } + + return null_element_; + } + + inline scope_element& get_active_element(const std::string& var_name, + const std::size_t index = std::numeric_limits::max()) + { + const std::size_t current_depth = parser_.state_.scope_depth; + + for (std::size_t i = 0; i < element_.size(); ++i) + { + scope_element& se = element_[i]; + + if (se.depth > current_depth) + continue; + else if ( + details::imatch(se.name, var_name) && + (se.index == index) && + (se.active) + ) + return se; + } + + return null_element_; + } + + inline bool add_element(const scope_element& se) + { + for (std::size_t i = 0; i < element_.size(); ++i) + { + scope_element& cse = element_[i]; + + if ( + details::imatch(cse.name, se.name) && + (cse.depth <= se.depth) && + (cse.index == se.index) && + (cse.size == se.size ) && + (cse.type == se.type ) && + (cse.active) + ) + return false; + } + + switch (se.type) + { + case scope_element::e_variable : total_local_symb_size_bytes_ += sizeof(T); + break; + + case scope_element::e_literal : total_local_symb_size_bytes_ += sizeof(T); + break; + + case scope_element::e_vector : total_local_symb_size_bytes_ += sizeof(T) * se.size; + break; + + default : break; + } + + element_.push_back(se); + std::sort(element_.begin(),element_.end()); + + return true; + } + + inline void deactivate(const std::size_t& scope_depth) + { + exprtk_debug(("deactivate() - Scope depth: %d\n", + static_cast(parser_.state_.scope_depth))); + + for (std::size_t i = 0; i < element_.size(); ++i) + { + scope_element& se = element_[i]; + + if (se.active && (se.depth >= scope_depth)) + { + exprtk_debug(("deactivate() - element[%02d] '%s'\n", + static_cast(i), + se.name.c_str())); + + se.active = false; + } + } + } + + inline void free_element(scope_element& se) + { + exprtk_debug(("free_element() - se[%s]\n", se.name.c_str())); + + switch (se.type) + { + case scope_element::e_literal : delete reinterpret_cast(se.data); + delete se.var_node; + break; + + case scope_element::e_variable : delete reinterpret_cast(se.data); + delete se.var_node; + break; + + case scope_element::e_vector : delete[] reinterpret_cast(se.data); + delete se.vec_node; + break; + + case scope_element::e_vecelem : delete se.var_node; + break; + + #ifndef exprtk_disable_string_capabilities + case scope_element::e_string : delete reinterpret_cast(se.data); + delete se.str_node; + break; + #endif + + default : return; + } + + se.clear(); + } + + inline void cleanup() + { + for (std::size_t i = 0; i < element_.size(); ++i) + { + free_element(element_[i]); + } + + element_.clear(); + + input_param_cnt_ = 0; + total_local_symb_size_bytes_ = 0; + } + + inline std::size_t total_local_symb_size_bytes() const + { + return total_local_symb_size_bytes_; + } + + inline std::size_t next_ip_index() + { + return ++input_param_cnt_; + } + + inline expression_node_ptr get_variable(const T& v) + { + for (std::size_t i = 0; i < element_.size(); ++i) + { + scope_element& se = element_[i]; + + if ( + se.active && + se.var_node && + details::is_variable_node(se.var_node) + ) + { + variable_node_ptr vn = reinterpret_cast(se.var_node); + + if (&(vn->ref()) == (&v)) + { + return se.var_node; + } + } + } + + return expression_node_ptr(0); + } + + inline std::string get_vector_name(const T* data) + { + for (std::size_t i = 0; i < element_.size(); ++i) + { + scope_element& se = element_[i]; + + if ( + se.active && + se.vec_node && + (se.vec_node->data() == data) + ) + { + return se.name; + } + } + + return "neo-vector"; + } + + private: + + scope_element_manager(const scope_element_manager&) exprtk_delete; + scope_element_manager& operator=(const scope_element_manager&) exprtk_delete; + + parser_t& parser_; + std::vector element_; + scope_element null_element_; + std::size_t input_param_cnt_; + std::size_t total_local_symb_size_bytes_; + }; + + class scope_handler + { + public: + + typedef parser parser_t; + + explicit scope_handler(parser& p) + : parser_(p) + { + parser_.state_.scope_depth++; + #ifdef exprtk_enable_debugging + const std::string depth(2 * parser_.state_.scope_depth,'-'); + exprtk_debug(("%s> Scope Depth: %02d\n", + depth.c_str(), + static_cast(parser_.state_.scope_depth))); + #endif + } + + ~scope_handler() + { + parser_.sem_.deactivate(parser_.state_.scope_depth); + parser_.state_.scope_depth--; + #ifdef exprtk_enable_debugging + const std::string depth(2 * parser_.state_.scope_depth,'-'); + exprtk_debug(("<%s Scope Depth: %02d\n", + depth.c_str(), + static_cast(parser_.state_.scope_depth))); + #endif + } + + private: + + scope_handler(const scope_handler&) exprtk_delete; + scope_handler& operator=(const scope_handler&) exprtk_delete; + + parser_t& parser_; + }; + + template + struct halfopen_range_policy + { + static inline bool is_within(const T_& v, const T_& begin, const T_& end) + { + assert(begin <= end); + return (begin <= v) && (v < end); + } + + static inline bool is_less(const T_& v, const T_& begin) + { + return (v < begin); + } + + static inline bool is_greater(const T_& v, const T_& end) + { + return (end <= v); + } + + static inline bool end_inclusive() + { + return false; + } + }; + + template + struct closed_range_policy + { + static inline bool is_within(const T_& v, const T_& begin, const T_& end) + { + assert(begin <= end); + return (begin <= v) && (v <= end); + } + + static inline bool is_less(const T_& v, const T_& begin) + { + return (v < begin); + } + + static inline bool is_greater(const T_& v, const T_& end) + { + return (end < v); + } + + static inline bool end_inclusive() + { + return true; + } + }; + + template > + class interval_container_t + { + public: + + typedef IntervalPointType interval_point_t; + typedef std::pair interval_t; + typedef std::map interval_map_t; + typedef typename interval_map_t::const_iterator interval_map_citr_t; + + std::size_t size() const + { + return interval_map_.size(); + } + + void reset() + { + interval_map_.clear(); + } + + bool in_interval(const interval_point_t point, interval_t& interval) const + { + interval_map_citr_t itr = RangePolicy::end_inclusive() ? + interval_map_.lower_bound(point): + interval_map_.upper_bound(point); + + for (; itr != interval_map_.end(); ++itr) + { + const interval_point_t& begin = itr->second.first; + const interval_point_t& end = itr->second.second; + + if (RangePolicy::is_within(point, begin, end)) + { + interval = interval_t(begin,end); + return true; + } + else if (RangePolicy::is_greater(point, end)) + { + break; + } + } + + return false; + } + + bool in_interval(const interval_point_t point) const + { + interval_t interval; + return in_interval(point,interval); + } + + bool add_interval(const interval_point_t begin, const interval_point_t end) + { + if ((end <= begin) || in_interval(begin) || in_interval(end)) + { + return false; + } + + interval_map_[end] = std::make_pair(begin, end); + + return true; + } + + bool add_interval(const interval_t interval) + { + return add_interval(interval.first, interval.second); + } + + private: + + interval_map_t interval_map_; + }; + + class stack_limit_handler + { + public: + + typedef parser parser_t; + + explicit stack_limit_handler(parser& p) + : parser_(p) + , limit_exceeded_(false) + { + if (++parser_.state_.stack_depth > parser_.settings_.max_stack_depth_) + { + limit_exceeded_ = true; + parser_.set_error(make_error( + parser_error::e_parser, + "ERR000 - Current stack depth " + details::to_str(parser_.state_.stack_depth) + + " exceeds maximum allowed stack depth of " + details::to_str(parser_.settings_.max_stack_depth_), + exprtk_error_location)); + } + } + + ~stack_limit_handler() + { + assert(parser_.state_.stack_depth > 0); + parser_.state_.stack_depth--; + } + + bool operator!() + { + return limit_exceeded_; + } + + private: + + stack_limit_handler(const stack_limit_handler&) exprtk_delete; + stack_limit_handler& operator=(const stack_limit_handler&) exprtk_delete; + + parser_t& parser_; + bool limit_exceeded_; + }; + + struct symtab_store + { + symbol_table_list_t symtab_list_; + + typedef typename symbol_table_t::local_data_t local_data_t; + typedef typename symbol_table_t::variable_ptr variable_ptr; + typedef typename symbol_table_t::function_ptr function_ptr; + #ifndef exprtk_disable_string_capabilities + typedef typename symbol_table_t::stringvar_ptr stringvar_ptr; + #endif + typedef typename symbol_table_t::vector_holder_ptr vector_holder_ptr; + typedef typename symbol_table_t::vararg_function_ptr vararg_function_ptr; + typedef typename symbol_table_t::generic_function_ptr generic_function_ptr; + + struct variable_context + { + variable_context() + : symbol_table(0) + , variable(0) + {} + + const symbol_table_t* symbol_table; + variable_ptr variable; + }; + + struct vector_context + { + vector_context() + : symbol_table(0) + , vector_holder(0) + {} + + const symbol_table_t* symbol_table; + vector_holder_ptr vector_holder; + }; + + #ifndef exprtk_disable_string_capabilities + struct string_context + { + string_context() + : symbol_table(0) + , str_var(0) + {} + + const symbol_table_t* symbol_table; + stringvar_ptr str_var; + }; + #endif + + inline bool empty() const + { + return symtab_list_.empty(); + } + + inline void clear() + { + symtab_list_.clear(); + } + + inline bool valid() const + { + if (!empty()) + { + for (std::size_t i = 0; i < symtab_list_.size(); ++i) + { + if (symtab_list_[i].valid()) + return true; + } + } + + return false; + } + + inline bool valid_symbol(const std::string& symbol) const + { + if (!symtab_list_.empty()) + return symtab_list_[0].valid_symbol(symbol); + else + return false; + } + + inline bool valid_function_name(const std::string& symbol) const + { + if (!symtab_list_.empty()) + return symtab_list_[0].valid_function(symbol); + else + return false; + } + + inline variable_context get_variable_context(const std::string& variable_name) const + { + variable_context result; + + if (valid_symbol(variable_name)) + { + for (std::size_t i = 0; i < symtab_list_.size(); ++i) + { + if (!symtab_list_[i].valid()) + { + continue; + } + + result.variable = local_data(i) + .variable_store.get(variable_name); + if (result.variable) + { + result.symbol_table = &symtab_list_[i]; + break; + } + } + } + + return result; + } + + inline variable_ptr get_variable(const std::string& variable_name) const + { + if (!valid_symbol(variable_name)) + return reinterpret_cast(0); + + variable_ptr result = reinterpret_cast(0); + + for (std::size_t i = 0; i < symtab_list_.size(); ++i) + { + if (!symtab_list_[i].valid()) + continue; + else + result = local_data(i) + .variable_store.get(variable_name); + + if (result) break; + } + + return result; + } + + inline variable_ptr get_variable(const T& var_ref) const + { + variable_ptr result = reinterpret_cast(0); + + for (std::size_t i = 0; i < symtab_list_.size(); ++i) + { + if (!symtab_list_[i].valid()) + continue; + else + result = local_data(i).variable_store + .get_from_varptr(reinterpret_cast(&var_ref)); + + if (result) break; + } + + return result; + } + + #ifndef exprtk_disable_string_capabilities + inline string_context get_string_context(const std::string& string_name) const + { + string_context result; + + if (!valid_symbol(string_name)) + return result; + + for (std::size_t i = 0; i < symtab_list_.size(); ++i) + { + if (!symtab_list_[i].valid()) + { + continue; + } + + result.str_var = local_data(i).stringvar_store.get(string_name); + + if (result.str_var) + { + result.symbol_table = &symtab_list_[i]; + break; + } + } + + return result; + } + + inline stringvar_ptr get_stringvar(const std::string& string_name) const + { + if (!valid_symbol(string_name)) + return reinterpret_cast(0); + + stringvar_ptr result = reinterpret_cast(0); + + for (std::size_t i = 0; i < symtab_list_.size(); ++i) + { + if (!symtab_list_[i].valid()) + continue; + else + result = local_data(i) + .stringvar_store.get(string_name); + + if (result) break; + } + + return result; + } + #endif + + inline function_ptr get_function(const std::string& function_name) const + { + if (!valid_function_name(function_name)) + return reinterpret_cast(0); + + function_ptr result = reinterpret_cast(0); + + for (std::size_t i = 0; i < symtab_list_.size(); ++i) + { + if (!symtab_list_[i].valid()) + continue; + else + result = local_data(i) + .function_store.get(function_name); + + if (result) break; + } + + return result; + } + + inline vararg_function_ptr get_vararg_function(const std::string& vararg_function_name) const + { + if (!valid_function_name(vararg_function_name)) + return reinterpret_cast(0); + + vararg_function_ptr result = reinterpret_cast(0); + + for (std::size_t i = 0; i < symtab_list_.size(); ++i) + { + if (!symtab_list_[i].valid()) + continue; + else + result = local_data(i) + .vararg_function_store.get(vararg_function_name); + + if (result) break; + } + + return result; + } + + inline generic_function_ptr get_generic_function(const std::string& function_name) const + { + if (!valid_function_name(function_name)) + return reinterpret_cast(0); + + generic_function_ptr result = reinterpret_cast(0); + + for (std::size_t i = 0; i < symtab_list_.size(); ++i) + { + if (!symtab_list_[i].valid()) + continue; + else + result = local_data(i) + .generic_function_store.get(function_name); + + if (result) break; + } + + return result; + } + + inline generic_function_ptr get_string_function(const std::string& function_name) const + { + if (!valid_function_name(function_name)) + return reinterpret_cast(0); + + generic_function_ptr result = reinterpret_cast(0); + + for (std::size_t i = 0; i < symtab_list_.size(); ++i) + { + if (!symtab_list_[i].valid()) + continue; + else + result = + local_data(i).string_function_store.get(function_name); + + if (result) break; + } + + return result; + } + + inline generic_function_ptr get_overload_function(const std::string& function_name) const + { + if (!valid_function_name(function_name)) + return reinterpret_cast(0); + + generic_function_ptr result = reinterpret_cast(0); + + for (std::size_t i = 0; i < symtab_list_.size(); ++i) + { + if (!symtab_list_[i].valid()) + continue; + else + result = + local_data(i).overload_function_store.get(function_name); + + if (result) break; + } + + return result; + } + + inline vector_context get_vector_context(const std::string& vector_name) const + { + vector_context result; + if (!valid_symbol(vector_name)) + return result; + + for (std::size_t i = 0; i < symtab_list_.size(); ++i) + { + if (!symtab_list_[i].valid()) + { + continue; + } + + result.vector_holder = local_data(i).vector_store.get(vector_name); + + if (result.vector_holder) + { + result.symbol_table = &symtab_list_[i]; + break; + } + } + + return result; + } + + inline vector_holder_ptr get_vector(const std::string& vector_name) const + { + if (!valid_symbol(vector_name)) + return reinterpret_cast(0); + + vector_holder_ptr result = reinterpret_cast(0); + + for (std::size_t i = 0; i < symtab_list_.size(); ++i) + { + if (!symtab_list_[i].valid()) + { + continue; + } + + result = local_data(i).vector_store.get(vector_name); + + if (result) + { + break; + } + } + + return result; + } + + inline bool is_constant_node(const std::string& symbol_name) const + { + if (!valid_symbol(symbol_name)) + return false; + + for (std::size_t i = 0; i < symtab_list_.size(); ++i) + { + if (!symtab_list_[i].valid()) + { + continue; + } + + if (local_data(i).variable_store.is_constant(symbol_name)) + { + return true; + } + } + + return false; + } + + #ifndef exprtk_disable_string_capabilities + inline bool is_constant_string(const std::string& symbol_name) const + { + if (!valid_symbol(symbol_name)) + return false; + + for (std::size_t i = 0; i < symtab_list_.size(); ++i) + { + if (!symtab_list_[i].valid()) + continue; + else if (!local_data(i).stringvar_store.symbol_exists(symbol_name)) + continue; + else if (local_data(i).stringvar_store.is_constant(symbol_name)) + return true; + } + + return false; + } + #endif + + inline bool symbol_exists(const std::string& symbol) const + { + for (std::size_t i = 0; i < symtab_list_.size(); ++i) + { + if (!symtab_list_[i].valid()) + { + continue; + } + + if (symtab_list_[i].symbol_exists(symbol)) + { + return true; + } + } + + return false; + } + + inline bool is_variable(const std::string& variable_name) const + { + for (std::size_t i = 0; i < symtab_list_.size(); ++i) + { + if (!symtab_list_[i].valid()) + continue; + else if ( + symtab_list_[i].local_data().variable_store + .symbol_exists(variable_name) + ) + return true; + } + + return false; + } + + #ifndef exprtk_disable_string_capabilities + inline bool is_stringvar(const std::string& stringvar_name) const + { + for (std::size_t i = 0; i < symtab_list_.size(); ++i) + { + if (!symtab_list_[i].valid()) + continue; + else if ( + symtab_list_[i].local_data().stringvar_store + .symbol_exists(stringvar_name) + ) + return true; + } + + return false; + } + + inline bool is_conststr_stringvar(const std::string& symbol_name) const + { + for (std::size_t i = 0; i < symtab_list_.size(); ++i) + { + if (!symtab_list_[i].valid()) + continue; + else if ( + symtab_list_[i].local_data().stringvar_store + .symbol_exists(symbol_name) + ) + { + return ( + local_data(i).stringvar_store.symbol_exists(symbol_name) || + local_data(i).stringvar_store.is_constant (symbol_name) + ); + + } + } + + return false; + } + #endif + + inline bool is_function(const std::string& function_name) const + { + for (std::size_t i = 0; i < symtab_list_.size(); ++i) + { + if (!symtab_list_[i].valid()) + continue; + else if ( + local_data(i).vararg_function_store + .symbol_exists(function_name) + ) + return true; + } + + return false; + } + + inline bool is_vararg_function(const std::string& vararg_function_name) const + { + for (std::size_t i = 0; i < symtab_list_.size(); ++i) + { + if (!symtab_list_[i].valid()) + continue; + else if ( + local_data(i).vararg_function_store + .symbol_exists(vararg_function_name) + ) + return true; + } + + return false; + } + + inline bool is_vector(const std::string& vector_name) const + { + for (std::size_t i = 0; i < symtab_list_.size(); ++i) + { + if (!symtab_list_[i].valid()) + continue; + else if ( + local_data(i).vector_store + .symbol_exists(vector_name) + ) + return true; + } + + return false; + } + + inline std::string get_variable_name(const expression_node_ptr& ptr) const + { + return local_data().variable_store.entity_name(ptr); + } + + inline std::string get_vector_name(const vector_holder_ptr& ptr) const + { + return local_data().vector_store.entity_name(ptr); + } + + #ifndef exprtk_disable_string_capabilities + inline std::string get_stringvar_name(const expression_node_ptr& ptr) const + { + return local_data().stringvar_store.entity_name(ptr); + } + + inline std::string get_conststr_stringvar_name(const expression_node_ptr& ptr) const + { + return local_data().stringvar_store.entity_name(ptr); + } + #endif + + inline local_data_t& local_data(const std::size_t& index = 0) + { + return symtab_list_[index].local_data(); + } + + inline const local_data_t& local_data(const std::size_t& index = 0) const + { + return symtab_list_[index].local_data(); + } + + inline symbol_table_t& get_symbol_table(const std::size_t& index = 0) + { + return symtab_list_[index]; + } + }; + + struct parser_state + { + parser_state() + : type_check_enabled(true) + { + reset(); + } + + void reset() + { + parsing_return_stmt = false; + parsing_break_stmt = false; + parsing_assert_stmt = false; + return_stmt_present = false; + side_effect_present = false; + scope_depth = 0; + stack_depth = 0; + parsing_loop_stmt_count = 0; + } + + #ifndef exprtk_enable_debugging + void activate_side_effect(const std::string&) + #else + void activate_side_effect(const std::string& source) + #endif + { + if (!side_effect_present) + { + side_effect_present = true; + + exprtk_debug(("activate_side_effect() - caller: %s\n", source.c_str())); + } + } + + bool parsing_return_stmt; + bool parsing_break_stmt; + bool parsing_assert_stmt; + bool return_stmt_present; + bool side_effect_present; + bool type_check_enabled; + std::size_t scope_depth; + std::size_t stack_depth; + std::size_t parsing_loop_stmt_count; + }; + + public: + + struct unknown_symbol_resolver + { + + enum usr_symbol_type + { + e_usr_unknown_type = 0, + e_usr_variable_type = 1, + e_usr_constant_type = 2 + }; + + enum usr_mode + { + e_usrmode_default = 0, + e_usrmode_extended = 1 + }; + + usr_mode mode; + + explicit unknown_symbol_resolver(const usr_mode m = e_usrmode_default) + : mode(m) + {} + + virtual ~unknown_symbol_resolver() + {} + + virtual bool process(const std::string& /*unknown_symbol*/, + usr_symbol_type& st, + T& default_value, + std::string& error_message) + { + if (e_usrmode_default != mode) + return false; + + st = e_usr_variable_type; + default_value = T(0); + error_message.clear(); + + return true; + } + + virtual bool process(const std::string& /* unknown_symbol */, + symbol_table_t& /* symbol_table */, + std::string& /* error_message */) + { + return false; + } + }; + + enum collect_type + { + e_ct_none = 0, + e_ct_variables = 1, + e_ct_functions = 2, + e_ct_assignments = 4 + }; + + enum symbol_type + { + e_st_unknown = 0, + e_st_variable = 1, + e_st_vector = 2, + e_st_vecelem = 3, + e_st_string = 4, + e_st_function = 5, + e_st_local_variable = 6, + e_st_local_vector = 7, + e_st_local_string = 8 + }; + + class dependent_entity_collector + { + public: + + typedef std::pair symbol_t; + typedef std::vector symbol_list_t; + + explicit dependent_entity_collector(const std::size_t options = e_ct_none) + : options_(options) + , collect_variables_ ((options_ & e_ct_variables ) == e_ct_variables ) + , collect_functions_ ((options_ & e_ct_functions ) == e_ct_functions ) + , collect_assignments_((options_ & e_ct_assignments) == e_ct_assignments) + , return_present_ (false) + , final_stmt_return_(false) + {} + + template class Sequence> + inline std::size_t symbols(Sequence& symbols_list) + { + if (!collect_variables_ && !collect_functions_) + return 0; + else if (symbol_name_list_.empty()) + return 0; + + for (std::size_t i = 0; i < symbol_name_list_.size(); ++i) + { + details::case_normalise(symbol_name_list_[i].first); + } + + std::sort(symbol_name_list_.begin(), symbol_name_list_.end()); + + std::unique_copy + ( + symbol_name_list_.begin(), + symbol_name_list_.end (), + std::back_inserter(symbols_list) + ); + + return symbols_list.size(); + } + + template class Sequence> + inline std::size_t assignment_symbols(Sequence& assignment_list) + { + if (!collect_assignments_) + return 0; + else if (assignment_name_list_.empty()) + return 0; + + for (std::size_t i = 0; i < assignment_name_list_.size(); ++i) + { + details::case_normalise(assignment_name_list_[i].first); + } + + std::sort(assignment_name_list_.begin(),assignment_name_list_.end()); + + std::unique_copy + ( + assignment_name_list_.begin(), + assignment_name_list_.end (), + std::back_inserter(assignment_list) + ); + + return assignment_list.size(); + } + + void clear() + { + symbol_name_list_ .clear(); + assignment_name_list_.clear(); + retparam_list_ .clear(); + return_present_ = false; + final_stmt_return_ = false; + } + + bool& collect_variables() + { + return collect_variables_; + } + + bool& collect_functions() + { + return collect_functions_; + } + + bool& collect_assignments() + { + return collect_assignments_; + } + + bool return_present() const + { + return return_present_; + } + + bool final_stmt_return() const + { + return final_stmt_return_; + } + + typedef std::vector retparam_list_t; + + retparam_list_t return_param_type_list() const + { + return retparam_list_; + } + + private: + + inline void add_symbol(const std::string& symbol, const symbol_type st) + { + switch (st) + { + case e_st_variable : + case e_st_vector : + case e_st_string : + case e_st_local_variable : + case e_st_local_vector : + case e_st_local_string : if (collect_variables_) + symbol_name_list_ + .push_back(std::make_pair(symbol, st)); + break; + + case e_st_function : if (collect_functions_) + symbol_name_list_ + .push_back(std::make_pair(symbol, st)); + break; + + default : return; + } + } + + inline void add_assignment(const std::string& symbol, const symbol_type st) + { + switch (st) + { + case e_st_variable : + case e_st_vector : + case e_st_string : if (collect_assignments_) + assignment_name_list_ + .push_back(std::make_pair(symbol, st)); + break; + + default : return; + } + } + + std::size_t options_; + bool collect_variables_; + bool collect_functions_; + bool collect_assignments_; + bool return_present_; + bool final_stmt_return_; + symbol_list_t symbol_name_list_; + symbol_list_t assignment_name_list_; + retparam_list_t retparam_list_; + + friend class parser; + }; + + class settings_store + { + private: + + typedef std::set disabled_entity_set_t; + typedef disabled_entity_set_t::iterator des_itr_t; + + public: + + enum settings_compilation_options + { + e_unknown = 0, + e_replacer = 1, + e_joiner = 2, + e_numeric_check = 4, + e_bracket_check = 8, + e_sequence_check = 16, + e_commutative_check = 32, + e_strength_reduction = 64, + e_disable_vardef = 128, + e_collect_vars = 256, + e_collect_funcs = 512, + e_collect_assings = 1024, + e_disable_usr_on_rsrvd = 2048, + e_disable_zero_return = 4096 + }; + + enum settings_base_funcs + { + e_bf_unknown = 0, + e_bf_abs , e_bf_acos , e_bf_acosh , e_bf_asin , + e_bf_asinh , e_bf_atan , e_bf_atan2 , e_bf_atanh , + e_bf_avg , e_bf_ceil , e_bf_clamp , e_bf_cos , + e_bf_cosh , e_bf_cot , e_bf_csc , e_bf_equal , + e_bf_erf , e_bf_erfc , e_bf_exp , e_bf_expm1 , + e_bf_floor , e_bf_frac , e_bf_hypot , e_bf_iclamp , + e_bf_like , e_bf_log , e_bf_log10 , e_bf_log1p , + e_bf_log2 , e_bf_logn , e_bf_mand , e_bf_max , + e_bf_min , e_bf_mod , e_bf_mor , e_bf_mul , + e_bf_ncdf , e_bf_pow , e_bf_root , e_bf_round , + e_bf_roundn , e_bf_sec , e_bf_sgn , e_bf_sin , + e_bf_sinc , e_bf_sinh , e_bf_sqrt , e_bf_sum , + e_bf_swap , e_bf_tan , e_bf_tanh , e_bf_trunc , + e_bf_not_equal , e_bf_inrange , e_bf_deg2grad , e_bf_deg2rad , + e_bf_rad2deg , e_bf_grad2deg + }; + + enum settings_control_structs + { + e_ctrl_unknown = 0, + e_ctrl_ifelse, + e_ctrl_switch, + e_ctrl_for_loop, + e_ctrl_while_loop, + e_ctrl_repeat_loop, + e_ctrl_return + }; + + enum settings_logic_opr + { + e_logic_unknown = 0, + e_logic_and, e_logic_nand , e_logic_nor , + e_logic_not, e_logic_or , e_logic_xnor, + e_logic_xor, e_logic_scand, e_logic_scor + }; + + enum settings_arithmetic_opr + { + e_arith_unknown = 0, + e_arith_add, e_arith_sub, e_arith_mul, + e_arith_div, e_arith_mod, e_arith_pow + }; + + enum settings_assignment_opr + { + e_assign_unknown = 0, + e_assign_assign, e_assign_addass, e_assign_subass, + e_assign_mulass, e_assign_divass, e_assign_modass + }; + + enum settings_inequality_opr + { + e_ineq_unknown = 0, + e_ineq_lt , e_ineq_lte, e_ineq_eq , + e_ineq_equal, e_ineq_ne , e_ineq_nequal, + e_ineq_gte , e_ineq_gt + }; + + static const std::size_t default_compile_all_opts = + e_replacer + + e_joiner + + e_numeric_check + + e_bracket_check + + e_sequence_check + + e_commutative_check + + e_strength_reduction; + + settings_store(const std::size_t compile_options = default_compile_all_opts) + : max_stack_depth_(400) + , max_node_depth_(10000) + , max_total_local_symbol_size_bytes_(2000000000) + , max_local_vector_size_(max_total_local_symbol_size_bytes_ / sizeof(T)) + { + load_compile_options(compile_options); + } + + settings_store& enable_all_base_functions() + { + disabled_func_set_.clear(); + return (*this); + } + + settings_store& enable_all_control_structures() + { + disabled_ctrl_set_.clear(); + return (*this); + } + + settings_store& enable_all_logic_ops() + { + disabled_logic_set_.clear(); + return (*this); + } + + settings_store& enable_all_arithmetic_ops() + { + disabled_arithmetic_set_.clear(); + return (*this); + } + + settings_store& enable_all_assignment_ops() + { + disabled_assignment_set_.clear(); + return (*this); + } + + settings_store& enable_all_inequality_ops() + { + disabled_inequality_set_.clear(); + return (*this); + } + + settings_store& enable_local_vardef() + { + disable_vardef_ = false; + return (*this); + } + + settings_store& enable_commutative_check() + { + enable_commutative_check_ = true; + return (*this); + } + + settings_store& enable_strength_reduction() + { + enable_strength_reduction_ = true; + return (*this); + } + + settings_store& disable_all_base_functions() + { + std::copy(details::base_function_list, + details::base_function_list + details::base_function_list_size, + std::insert_iterator + (disabled_func_set_, disabled_func_set_.begin())); + return (*this); + } + + settings_store& disable_all_control_structures() + { + std::copy(details::cntrl_struct_list, + details::cntrl_struct_list + details::cntrl_struct_list_size, + std::insert_iterator + (disabled_ctrl_set_, disabled_ctrl_set_.begin())); + return (*this); + } + + settings_store& disable_all_logic_ops() + { + std::copy(details::logic_ops_list, + details::logic_ops_list + details::logic_ops_list_size, + std::insert_iterator + (disabled_logic_set_, disabled_logic_set_.begin())); + return (*this); + } + + settings_store& disable_all_arithmetic_ops() + { + std::copy(details::arithmetic_ops_list, + details::arithmetic_ops_list + details::arithmetic_ops_list_size, + std::insert_iterator + (disabled_arithmetic_set_, disabled_arithmetic_set_.begin())); + return (*this); + } + + settings_store& disable_all_assignment_ops() + { + std::copy(details::assignment_ops_list, + details::assignment_ops_list + details::assignment_ops_list_size, + std::insert_iterator + (disabled_assignment_set_, disabled_assignment_set_.begin())); + return (*this); + } + + settings_store& disable_all_inequality_ops() + { + std::copy(details::inequality_ops_list, + details::inequality_ops_list + details::inequality_ops_list_size, + std::insert_iterator + (disabled_inequality_set_, disabled_inequality_set_.begin())); + return (*this); + } + + settings_store& disable_local_vardef() + { + disable_vardef_ = true; + return (*this); + } + + settings_store& disable_commutative_check() + { + enable_commutative_check_ = false; + return (*this); + } + + settings_store& disable_strength_reduction() + { + enable_strength_reduction_ = false; + return (*this); + } + + bool replacer_enabled () const { return enable_replacer_; } + bool commutative_check_enabled () const { return enable_commutative_check_; } + bool joiner_enabled () const { return enable_joiner_; } + bool numeric_check_enabled () const { return enable_numeric_check_; } + bool bracket_check_enabled () const { return enable_bracket_check_; } + bool sequence_check_enabled () const { return enable_sequence_check_; } + bool strength_reduction_enabled () const { return enable_strength_reduction_; } + bool collect_variables_enabled () const { return enable_collect_vars_; } + bool collect_functions_enabled () const { return enable_collect_funcs_; } + bool collect_assignments_enabled() const { return enable_collect_assings_; } + bool vardef_disabled () const { return disable_vardef_; } + bool rsrvd_sym_usr_disabled () const { return disable_rsrvd_sym_usr_; } + bool zero_return_disabled () const { return disable_zero_return_; } + + bool function_enabled(const std::string& function_name) const + { + if (disabled_func_set_.empty()) + return true; + else + return (disabled_func_set_.end() == disabled_func_set_.find(function_name)); + } + + bool control_struct_enabled(const std::string& control_struct) const + { + if (disabled_ctrl_set_.empty()) + return true; + else + return (disabled_ctrl_set_.end() == disabled_ctrl_set_.find(control_struct)); + } + + bool logic_enabled(const std::string& logic_operation) const + { + if (disabled_logic_set_.empty()) + return true; + else + return (disabled_logic_set_.end() == disabled_logic_set_.find(logic_operation)); + } + + bool arithmetic_enabled(const details::operator_type& arithmetic_operation) const + { + if (disabled_logic_set_.empty()) + return true; + else + return disabled_arithmetic_set_.end() == disabled_arithmetic_set_ + .find(arith_opr_to_string(arithmetic_operation)); + } + + bool assignment_enabled(const details::operator_type& assignment) const + { + if (disabled_assignment_set_.empty()) + return true; + else + return disabled_assignment_set_.end() == disabled_assignment_set_ + .find(assign_opr_to_string(assignment)); + } + + bool inequality_enabled(const details::operator_type& inequality) const + { + if (disabled_inequality_set_.empty()) + return true; + else + return disabled_inequality_set_.end() == disabled_inequality_set_ + .find(inequality_opr_to_string(inequality)); + } + + bool function_disabled(const std::string& function_name) const + { + if (disabled_func_set_.empty()) + return false; + else + return (disabled_func_set_.end() != disabled_func_set_.find(function_name)); + } + + bool control_struct_disabled(const std::string& control_struct) const + { + if (disabled_ctrl_set_.empty()) + return false; + else + return (disabled_ctrl_set_.end() != disabled_ctrl_set_.find(control_struct)); + } + + bool logic_disabled(const std::string& logic_operation) const + { + if (disabled_logic_set_.empty()) + return false; + else + return (disabled_logic_set_.end() != disabled_logic_set_.find(logic_operation)); + } + + bool assignment_disabled(const details::operator_type assignment_operation) const + { + if (disabled_assignment_set_.empty()) + return false; + else + return disabled_assignment_set_.end() != disabled_assignment_set_ + .find(assign_opr_to_string(assignment_operation)); + } + + bool logic_disabled(const details::operator_type logic_operation) const + { + if (disabled_logic_set_.empty()) + return false; + else + return disabled_logic_set_.end() != disabled_logic_set_ + .find(logic_opr_to_string(logic_operation)); + } + + bool arithmetic_disabled(const details::operator_type arithmetic_operation) const + { + if (disabled_arithmetic_set_.empty()) + return false; + else + return disabled_arithmetic_set_.end() != disabled_arithmetic_set_ + .find(arith_opr_to_string(arithmetic_operation)); + } + + bool inequality_disabled(const details::operator_type& inequality) const + { + if (disabled_inequality_set_.empty()) + return false; + else + return disabled_inequality_set_.end() != disabled_inequality_set_ + .find(inequality_opr_to_string(inequality)); + } + + settings_store& disable_base_function(const settings_base_funcs bf) + { + if ( + (e_bf_unknown != bf) && + (static_cast(bf) < (details::base_function_list_size + 1)) + ) + { + disabled_func_set_.insert(details::base_function_list[bf - 1]); + } + + return (*this); + } + + settings_store& disable_control_structure(const settings_control_structs ctrl_struct) + { + if ( + (e_ctrl_unknown != ctrl_struct) && + (static_cast(ctrl_struct) < (details::cntrl_struct_list_size + 1)) + ) + { + disabled_ctrl_set_.insert(details::cntrl_struct_list[ctrl_struct - 1]); + } + + return (*this); + } + + settings_store& disable_logic_operation(const settings_logic_opr logic) + { + if ( + (e_logic_unknown != logic) && + (static_cast(logic) < (details::logic_ops_list_size + 1)) + ) + { + disabled_logic_set_.insert(details::logic_ops_list[logic - 1]); + } + + return (*this); + } + + settings_store& disable_arithmetic_operation(const settings_arithmetic_opr arithmetic) + { + if ( + (e_arith_unknown != arithmetic) && + (static_cast(arithmetic) < (details::arithmetic_ops_list_size + 1)) + ) + { + disabled_arithmetic_set_.insert(details::arithmetic_ops_list[arithmetic - 1]); + } + + return (*this); + } + + settings_store& disable_assignment_operation(const settings_assignment_opr assignment) + { + if ( + (e_assign_unknown != assignment) && + (static_cast(assignment) < (details::assignment_ops_list_size + 1)) + ) + { + disabled_assignment_set_.insert(details::assignment_ops_list[assignment - 1]); + } + + return (*this); + } + + settings_store& disable_inequality_operation(const settings_inequality_opr inequality) + { + if ( + (e_ineq_unknown != inequality) && + (static_cast(inequality) < (details::inequality_ops_list_size + 1)) + ) + { + disabled_inequality_set_.insert(details::inequality_ops_list[inequality - 1]); + } + + return (*this); + } + + settings_store& enable_base_function(const settings_base_funcs bf) + { + if ( + (e_bf_unknown != bf) && + (static_cast(bf) < (details::base_function_list_size + 1)) + ) + { + const des_itr_t itr = disabled_func_set_.find(details::base_function_list[bf - 1]); + + if (disabled_func_set_.end() != itr) + { + disabled_func_set_.erase(itr); + } + } + + return (*this); + } + + settings_store& enable_control_structure(const settings_control_structs ctrl_struct) + { + if ( + (e_ctrl_unknown != ctrl_struct) && + (static_cast(ctrl_struct) < (details::cntrl_struct_list_size + 1)) + ) + { + const des_itr_t itr = disabled_ctrl_set_.find(details::cntrl_struct_list[ctrl_struct - 1]); + + if (disabled_ctrl_set_.end() != itr) + { + disabled_ctrl_set_.erase(itr); + } + } + + return (*this); + } + + settings_store& enable_logic_operation(const settings_logic_opr logic) + { + if ( + (e_logic_unknown != logic) && + (static_cast(logic) < (details::logic_ops_list_size + 1)) + ) + { + const des_itr_t itr = disabled_logic_set_.find(details::logic_ops_list[logic - 1]); + + if (disabled_logic_set_.end() != itr) + { + disabled_logic_set_.erase(itr); + } + } + + return (*this); + } + + settings_store& enable_arithmetic_operation(const settings_arithmetic_opr arithmetic) + { + if ( + (e_arith_unknown != arithmetic) && + (static_cast(arithmetic) < (details::arithmetic_ops_list_size + 1)) + ) + { + const des_itr_t itr = disabled_arithmetic_set_.find(details::arithmetic_ops_list[arithmetic - 1]); + + if (disabled_arithmetic_set_.end() != itr) + { + disabled_arithmetic_set_.erase(itr); + } + } + + return (*this); + } + + settings_store& enable_assignment_operation(const settings_assignment_opr assignment) + { + if ( + (e_assign_unknown != assignment) && + (static_cast(assignment) < (details::assignment_ops_list_size + 1)) + ) + { + const des_itr_t itr = disabled_assignment_set_.find(details::assignment_ops_list[assignment - 1]); + + if (disabled_assignment_set_.end() != itr) + { + disabled_assignment_set_.erase(itr); + } + } + + return (*this); + } + + settings_store& enable_inequality_operation(const settings_inequality_opr inequality) + { + if ( + (e_ineq_unknown != inequality) && + (static_cast(inequality) < (details::inequality_ops_list_size + 1)) + ) + { + const des_itr_t itr = disabled_inequality_set_.find(details::inequality_ops_list[inequality - 1]); + + if (disabled_inequality_set_.end() != itr) + { + disabled_inequality_set_.erase(itr); + } + } + + return (*this); + } + + void set_max_stack_depth(const std::size_t max_stack_depth) + { + max_stack_depth_ = max_stack_depth; + } + + void set_max_node_depth(const std::size_t max_node_depth) + { + max_node_depth_ = max_node_depth; + } + + void set_max_local_vector_size(const std::size_t max_local_vector_size) + { + max_local_vector_size_ = max_local_vector_size; + } + + void set_max_total_local_symbol_size_bytes(const std::size_t max_total_lcl_symb_size) + { + max_total_local_symbol_size_bytes_ = max_total_lcl_symb_size; + } + + std::size_t max_stack_depth() const + { + return max_stack_depth_; + } + + std::size_t max_node_depth() const + { + return max_node_depth_; + } + + std::size_t max_local_vector_size() const + { + return max_local_vector_size_; + } + + std::size_t max_total_local_symbol_size_bytes() const + { + return max_total_local_symbol_size_bytes_; + } + + private: + + void load_compile_options(const std::size_t compile_options) + { + enable_replacer_ = (compile_options & e_replacer ) == e_replacer; + enable_joiner_ = (compile_options & e_joiner ) == e_joiner; + enable_numeric_check_ = (compile_options & e_numeric_check ) == e_numeric_check; + enable_bracket_check_ = (compile_options & e_bracket_check ) == e_bracket_check; + enable_sequence_check_ = (compile_options & e_sequence_check ) == e_sequence_check; + enable_commutative_check_ = (compile_options & e_commutative_check ) == e_commutative_check; + enable_strength_reduction_ = (compile_options & e_strength_reduction ) == e_strength_reduction; + enable_collect_vars_ = (compile_options & e_collect_vars ) == e_collect_vars; + enable_collect_funcs_ = (compile_options & e_collect_funcs ) == e_collect_funcs; + enable_collect_assings_ = (compile_options & e_collect_assings ) == e_collect_assings; + disable_vardef_ = (compile_options & e_disable_vardef ) == e_disable_vardef; + disable_rsrvd_sym_usr_ = (compile_options & e_disable_usr_on_rsrvd) == e_disable_usr_on_rsrvd; + disable_zero_return_ = (compile_options & e_disable_zero_return ) == e_disable_zero_return; + } + + std::string assign_opr_to_string(details::operator_type opr) const + { + switch (opr) + { + case details::e_assign : return ":="; + case details::e_addass : return "+="; + case details::e_subass : return "-="; + case details::e_mulass : return "*="; + case details::e_divass : return "/="; + case details::e_modass : return "%="; + default : return "" ; + } + } + + std::string arith_opr_to_string(details::operator_type opr) const + { + switch (opr) + { + case details::e_add : return "+"; + case details::e_sub : return "-"; + case details::e_mul : return "*"; + case details::e_div : return "/"; + case details::e_mod : return "%"; + case details::e_pow : return "^"; + default : return "" ; + } + } + + std::string inequality_opr_to_string(details::operator_type opr) const + { + switch (opr) + { + case details::e_lt : return "<" ; + case details::e_lte : return "<="; + case details::e_eq : return "=="; + case details::e_equal : return "=" ; + case details::e_ne : return "!="; + case details::e_nequal: return "<>"; + case details::e_gte : return ">="; + case details::e_gt : return ">" ; + default : return "" ; + } + } + + std::string logic_opr_to_string(details::operator_type opr) const + { + switch (opr) + { + case details::e_and : return "and" ; + case details::e_or : return "or" ; + case details::e_xor : return "xor" ; + case details::e_nand : return "nand"; + case details::e_nor : return "nor" ; + case details::e_xnor : return "xnor"; + case details::e_notl : return "not" ; + default : return "" ; + } + } + + bool enable_replacer_; + bool enable_joiner_; + bool enable_numeric_check_; + bool enable_bracket_check_; + bool enable_sequence_check_; + bool enable_commutative_check_; + bool enable_strength_reduction_; + bool enable_collect_vars_; + bool enable_collect_funcs_; + bool enable_collect_assings_; + bool disable_vardef_; + bool disable_rsrvd_sym_usr_; + bool disable_zero_return_; + + disabled_entity_set_t disabled_func_set_ ; + disabled_entity_set_t disabled_ctrl_set_ ; + disabled_entity_set_t disabled_logic_set_; + disabled_entity_set_t disabled_arithmetic_set_; + disabled_entity_set_t disabled_assignment_set_; + disabled_entity_set_t disabled_inequality_set_; + + std::size_t max_stack_depth_; + std::size_t max_node_depth_; + std::size_t max_total_local_symbol_size_bytes_; + std::size_t max_local_vector_size_; + + friend class parser; + }; + + typedef settings_store settings_t; + + explicit parser(const settings_t& settings = settings_t()) + : settings_(settings) + , resolve_unknown_symbol_(false) + , results_context_(0) + , unknown_symbol_resolver_(reinterpret_cast(0)) + #ifdef _MSC_VER + #pragma warning(push) + #pragma warning (disable:4355) + #endif + , sem_(*this) + #ifdef _MSC_VER + #pragma warning(pop) + #endif + , operator_joiner_2_(2) + , operator_joiner_3_(3) + , loop_runtime_check_(0) + , vector_access_runtime_check_(0) + , compilation_check_ptr_(0) + , assert_check_(0) + { + init_precompilation(); + + load_operations_map (base_ops_map_ ); + load_unary_operations_map (unary_op_map_ ); + load_binary_operations_map (binary_op_map_ ); + load_inv_binary_operations_map(inv_binary_op_map_); + load_sf3_map (sf3_map_ ); + load_sf4_map (sf4_map_ ); + + expression_generator_.init_synthesize_map(); + expression_generator_.set_parser(*this); + expression_generator_.set_uom (unary_op_map_ ); + expression_generator_.set_bom (binary_op_map_ ); + expression_generator_.set_ibom(inv_binary_op_map_); + expression_generator_.set_sf3m(sf3_map_ ); + expression_generator_.set_sf4m(sf4_map_ ); + expression_generator_.set_strength_reduction_state(settings_.strength_reduction_enabled()); + } + + ~parser() + {} + + inline void init_precompilation() + { + dec_.collect_variables() = + settings_.collect_variables_enabled(); + + dec_.collect_functions() = + settings_.collect_functions_enabled(); + + dec_.collect_assignments() = + settings_.collect_assignments_enabled(); + + if (settings_.replacer_enabled()) + { + symbol_replacer_.clear(); + symbol_replacer_.add_replace("true" , "1", lexer::token::e_number); + symbol_replacer_.add_replace("false", "0", lexer::token::e_number); + helper_assembly_.token_modifier_list.clear(); + helper_assembly_.register_modifier(&symbol_replacer_); + } + + if (settings_.commutative_check_enabled()) + { + for (std::size_t i = 0; i < details::reserved_words_size; ++i) + { + commutative_inserter_.ignore_symbol(details::reserved_words[i]); + } + + helper_assembly_.token_inserter_list.clear(); + helper_assembly_.register_inserter(&commutative_inserter_); + } + + if (settings_.joiner_enabled()) + { + helper_assembly_.token_joiner_list.clear(); + helper_assembly_.register_joiner(&operator_joiner_2_); + helper_assembly_.register_joiner(&operator_joiner_3_); + } + + if ( + settings_.numeric_check_enabled () || + settings_.bracket_check_enabled () || + settings_.sequence_check_enabled() + ) + { + helper_assembly_.token_scanner_list.clear(); + + if (settings_.numeric_check_enabled()) + { + helper_assembly_.register_scanner(&numeric_checker_); + } + + if (settings_.bracket_check_enabled()) + { + helper_assembly_.register_scanner(&bracket_checker_); + } + + if (settings_.sequence_check_enabled()) + { + helper_assembly_.register_scanner(&sequence_validator_ ); + helper_assembly_.register_scanner(&sequence_validator_3tkns_); + } + } + } + + inline bool compile(const std::string& expression_string, expression& expr) + { + state_ .reset(); + error_list_ .clear(); + brkcnt_list_ .clear(); + synthesis_error_ .clear(); + immutable_memory_map_.reset(); + immutable_symtok_map_.clear(); + current_state_stack_ .clear(); + assert_ids_ .clear(); + sem_ .cleanup(); + + return_cleanup(); + + if (!valid_settings()) + { + return false; + } + + expression_generator_.set_allocator(node_allocator_); + + if (expression_string.empty()) + { + set_error(make_error( + parser_error::e_syntax, + "ERR001 - Empty expression!", + exprtk_error_location)); + + return false; + } + + if (!init(expression_string)) + { + process_lexer_errors(); + return false; + } + + if (lexer().empty()) + { + set_error(make_error( + parser_error::e_syntax, + "ERR002 - Empty expression!", + exprtk_error_location)); + + return false; + } + + if (halt_compilation_check()) + { + exprtk_debug(("halt_compilation_check() - compile checkpoint 0\n")); + sem_.cleanup(); + return false; + } + + if (!run_assemblies()) + { + sem_.cleanup(); + return false; + } + + if (halt_compilation_check()) + { + exprtk_debug(("halt_compilation_check() - compile checkpoint 1\n")); + sem_.cleanup(); + return false; + } + + symtab_store_.symtab_list_ = expr.get_symbol_table_list(); + dec_.clear(); + + lexer().begin(); + + next_token(); + + expression_node_ptr e = parse_corpus(); + + if ((0 != e) && (token_t::e_eof == current_token().type)) + { + bool* retinvk_ptr = 0; + + if (state_.return_stmt_present) + { + dec_.return_present_ = true; + + e = expression_generator_ + .return_envelope(e, results_context_, retinvk_ptr); + } + + expr.set_expression(e); + expr.set_retinvk(retinvk_ptr); + + register_local_vars(expr); + register_return_results(expr); + + return !(!expr); + } + else + { + if (error_list_.empty()) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR003 - Invalid expression encountered", + exprtk_error_location)); + } + + if ((0 != e) && branch_deletable(e)) + { + destroy_node(e); + } + + dec_.clear (); + sem_.cleanup (); + return_cleanup(); + + return false; + } + } + + inline expression_t compile(const std::string& expression_string, symbol_table_t& symtab) + { + expression_t expression; + expression.register_symbol_table(symtab); + compile(expression_string,expression); + return expression; + } + + void process_lexer_errors() + { + for (std::size_t i = 0; i < lexer().size(); ++i) + { + if (lexer()[i].is_error()) + { + std::string diagnostic = "ERR004 - "; + + switch (lexer()[i].type) + { + case lexer::token::e_error : diagnostic += "General token error"; + break; + + case lexer::token::e_err_symbol : diagnostic += "Symbol error"; + break; + + case lexer::token::e_err_number : diagnostic += "Invalid numeric token"; + break; + + case lexer::token::e_err_string : diagnostic += "Invalid string token"; + break; + + case lexer::token::e_err_sfunc : diagnostic += "Invalid special function token"; + break; + + default : diagnostic += "Unknown compiler error"; + } + + set_error(make_error( + parser_error::e_lexer, + lexer()[i], + diagnostic + ": " + lexer()[i].value, + exprtk_error_location)); + } + } + } + + inline bool run_assemblies() + { + if (settings_.commutative_check_enabled()) + { + helper_assembly_.run_inserters(lexer()); + } + + if (settings_.joiner_enabled()) + { + helper_assembly_.run_joiners(lexer()); + } + + if (settings_.replacer_enabled()) + { + helper_assembly_.run_modifiers(lexer()); + } + + if ( + settings_.numeric_check_enabled () || + settings_.bracket_check_enabled () || + settings_.sequence_check_enabled() + ) + { + if (!helper_assembly_.run_scanners(lexer())) + { + if (helper_assembly_.error_token_scanner) + { + lexer::helper::bracket_checker* bracket_checker_ptr = 0; + lexer::helper::numeric_checker* numeric_checker_ptr = 0; + lexer::helper::sequence_validator* sequence_validator_ptr = 0; + lexer::helper::sequence_validator_3tokens* sequence_validator3_ptr = 0; + + if (0 != (bracket_checker_ptr = dynamic_cast(helper_assembly_.error_token_scanner))) + { + set_error(make_error( + parser_error::e_token, + bracket_checker_ptr->error_token(), + "ERR005 - Mismatched brackets: '" + bracket_checker_ptr->error_token().value + "'", + exprtk_error_location)); + } + else if (0 != (numeric_checker_ptr = dynamic_cast*>(helper_assembly_.error_token_scanner))) + { + for (std::size_t i = 0; i < numeric_checker_ptr->error_count(); ++i) + { + lexer::token error_token = lexer()[numeric_checker_ptr->error_index(i)]; + + set_error(make_error( + parser_error::e_token, + error_token, + "ERR006 - Invalid numeric token: '" + error_token.value + "'", + exprtk_error_location)); + } + + if (numeric_checker_ptr->error_count()) + { + numeric_checker_ptr->clear_errors(); + } + } + else if (0 != (sequence_validator_ptr = dynamic_cast(helper_assembly_.error_token_scanner))) + { + for (std::size_t i = 0; i < sequence_validator_ptr->error_count(); ++i) + { + std::pair error_token = sequence_validator_ptr->error(i); + + set_error(make_error( + parser_error::e_token, + error_token.first, + "ERR007 - Invalid token sequence: '" + + error_token.first.value + "' and '" + + error_token.second.value + "'", + exprtk_error_location)); + } + + if (sequence_validator_ptr->error_count()) + { + sequence_validator_ptr->clear_errors(); + } + } + else if (0 != (sequence_validator3_ptr = dynamic_cast(helper_assembly_.error_token_scanner))) + { + for (std::size_t i = 0; i < sequence_validator3_ptr->error_count(); ++i) + { + std::pair error_token = sequence_validator3_ptr->error(i); + + set_error(make_error( + parser_error::e_token, + error_token.first, + "ERR008 - Invalid token sequence: '" + + error_token.first.value + "' and '" + + error_token.second.value + "'", + exprtk_error_location)); + } + + if (sequence_validator3_ptr->error_count()) + { + sequence_validator3_ptr->clear_errors(); + } + } + } + + return false; + } + } + + return true; + } + + inline settings_store& settings() + { + return settings_; + } + + inline parser_error::type get_error(const std::size_t& index) const + { + if (index < error_list_.size()) + { + return error_list_[index]; + } + + throw std::invalid_argument("parser::get_error() - Invalid error index specified"); + } + + inline std::string error() const + { + if (!error_list_.empty()) + { + return error_list_[0].diagnostic; + } + else + return std::string("No Error"); + } + + inline std::size_t error_count() const + { + return error_list_.size(); + } + + inline dependent_entity_collector& dec() + { + return dec_; + } + + inline std::size_t total_local_symbol_size_bytes() const + { + return sem_.total_local_symb_size_bytes(); + } + + inline bool replace_symbol(const std::string& old_symbol, const std::string& new_symbol) + { + if (!settings_.replacer_enabled()) + return false; + else if (details::is_reserved_word(old_symbol)) + return false; + else + return symbol_replacer_.add_replace(old_symbol,new_symbol,lexer::token::e_symbol); + } + + inline bool remove_replace_symbol(const std::string& symbol) + { + if (!settings_.replacer_enabled()) + return false; + else if (details::is_reserved_word(symbol)) + return false; + else + return symbol_replacer_.remove(symbol); + } + + inline void enable_unknown_symbol_resolver(unknown_symbol_resolver* usr = reinterpret_cast(0)) + { + resolve_unknown_symbol_ = true; + + if (usr) + unknown_symbol_resolver_ = usr; + else + unknown_symbol_resolver_ = &default_usr_; + } + + inline void enable_unknown_symbol_resolver(unknown_symbol_resolver& usr) + { + enable_unknown_symbol_resolver(&usr); + } + + inline void disable_unknown_symbol_resolver() + { + resolve_unknown_symbol_ = false; + unknown_symbol_resolver_ = &default_usr_; + } + + inline void register_loop_runtime_check(loop_runtime_check& lrtchk) + { + loop_runtime_check_ = &lrtchk; + } + + inline void register_vector_access_runtime_check(vector_access_runtime_check& vartchk) + { + vector_access_runtime_check_ = &vartchk; + } + + inline void register_compilation_timeout_check(compilation_check& compchk) + { + compilation_check_ptr_ = &compchk; + } + + inline void register_assert_check(assert_check& assrt_chck) + { + assert_check_ = &assrt_chck; + } + + inline void clear_loop_runtime_check() + { + loop_runtime_check_ = loop_runtime_check_ptr(0); + } + + inline void clear_vector_access_runtime_check() + { + vector_access_runtime_check_ = vector_access_runtime_check_ptr(0); + } + + inline void clear_compilation_timeout_check() + { + compilation_check_ptr_ = compilation_check_ptr(0); + } + + inline void clear_assert_check() + { + assert_check_ = assert_check_ptr(0); + } + + private: + + inline bool valid_base_operation(const std::string& symbol) const + { + const std::size_t length = symbol.size(); + + if ( + (length < 3) || // Shortest base op symbol length + (length > 9) // Longest base op symbol length + ) + return false; + else + return settings_.function_enabled(symbol) && + (base_ops_map_.end() != base_ops_map_.find(symbol)); + } + + inline bool valid_vararg_operation(const std::string& symbol) const + { + static const std::string s_sum = "sum" ; + static const std::string s_mul = "mul" ; + static const std::string s_avg = "avg" ; + static const std::string s_min = "min" ; + static const std::string s_max = "max" ; + static const std::string s_mand = "mand"; + static const std::string s_mor = "mor" ; + static const std::string s_multi = "~" ; + static const std::string s_mswitch = "[*]" ; + + return + ( + details::imatch(symbol,s_sum ) || + details::imatch(symbol,s_mul ) || + details::imatch(symbol,s_avg ) || + details::imatch(symbol,s_min ) || + details::imatch(symbol,s_max ) || + details::imatch(symbol,s_mand ) || + details::imatch(symbol,s_mor ) || + details::imatch(symbol,s_multi ) || + details::imatch(symbol,s_mswitch) + ) && + settings_.function_enabled(symbol); + } + + bool is_invalid_logic_operation(const details::operator_type operation) const + { + return settings_.logic_disabled(operation); + } + + bool is_invalid_arithmetic_operation(const details::operator_type operation) const + { + return settings_.arithmetic_disabled(operation); + } + + bool is_invalid_assignment_operation(const details::operator_type operation) const + { + return settings_.assignment_disabled(operation); + } + + bool is_invalid_inequality_operation(const details::operator_type operation) const + { + return settings_.inequality_disabled(operation); + } + + #ifdef exprtk_enable_debugging + inline void next_token() + { + const std::string ct_str = current_token().value; + const std::size_t ct_pos = current_token().position; + parser_helper::next_token(); + const std::string depth(2 * state_.scope_depth,' '); + exprtk_debug(("%s" + "prev[%s | %04d] --> curr[%s | %04d] stack_level: %3d\n", + depth.c_str(), + ct_str.c_str(), + static_cast(ct_pos), + current_token().value.c_str(), + static_cast(current_token().position), + static_cast(state_.stack_depth))); + } + #endif + + inline expression_node_ptr parse_corpus() + { + std::vector arg_list; + std::vector side_effect_list; + + scoped_vec_delete svd((*this), arg_list); + + lexer::token begin_token; + lexer::token end_token; + + for ( ; ; ) + { + state_.side_effect_present = false; + + begin_token = current_token(); + + expression_node_ptr arg = parse_expression(); + + if (0 == arg) + { + if (error_list_.empty()) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR009 - Invalid expression encountered", + exprtk_error_location)); + } + + return error_node(); + } + else + { + arg_list.push_back(arg); + + side_effect_list.push_back(state_.side_effect_present); + + end_token = current_token(); + + const std::string sub_expr = construct_subexpr(begin_token, end_token); + + exprtk_debug(("parse_corpus(%02d) Subexpr: %s\n", + static_cast(arg_list.size() - 1), + sub_expr.c_str())); + + exprtk_debug(("parse_corpus(%02d) - Side effect present: %s\n", + static_cast(arg_list.size() - 1), + state_.side_effect_present ? "true" : "false")); + + exprtk_debug(("-------------------------------------------------\n")); + } + + if (token_is(token_t::e_eof,prsrhlpr_t::e_hold)) + { + if (lexer().finished()) + break; + else + next_token(); + } + else if ( + !settings_.commutative_check_enabled() && + ( + current_token().type == token_t::e_symbol || + current_token().type == token_t::e_number || + current_token().type == token_t::e_string || + token_is_bracket(prsrhlpr_t::e_hold) + ) + ) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR010 - Invalid syntax '" + current_token().value + "' possible missing operator or context", + exprtk_error_location)); + + return error_node(); + } + } + + if ( + !arg_list.empty() && + is_return_node(arg_list.back()) + ) + { + dec_.final_stmt_return_ = true; + } + + const expression_node_ptr result = simplify(arg_list,side_effect_list); + + svd.delete_ptr = (0 == result); + + return result; + } + + std::string construct_subexpr(lexer::token& begin_token, + lexer::token& end_token, + const bool cleanup_whitespace = true) + { + std::string result = lexer().substr(begin_token.position,end_token.position); + if (cleanup_whitespace) + { + for (std::size_t i = 0; i < result.size(); ++i) + { + if (details::is_whitespace(result[i])) result[i] = ' '; + } + } + + return result; + } + + static const precedence_level default_precedence = e_level00; + + struct state_t + { + inline void set(const precedence_level& l, + const precedence_level& r, + const details::operator_type& o, + const token_t tkn = token_t()) + { + left = l; + right = r; + operation = o; + token = tkn; + } + + inline void reset() + { + left = e_level00; + right = e_level00; + operation = details::e_default; + } + + precedence_level left; + precedence_level right; + details::operator_type operation; + token_t token; + }; + + inline void push_current_state(const state_t current_state) + { + current_state_stack_.push_back(current_state); + } + + inline void pop_current_state() + { + if (!current_state_stack_.empty()) + { + current_state_stack_.pop_back(); + } + } + + inline state_t current_state() const + { + return (!current_state_stack_.empty()) ? + current_state_stack_.back() : + state_t(); + } + + inline bool halt_compilation_check() + { + compilation_check::compilation_context context; + + if (compilation_check_ptr_ && !compilation_check_ptr_->continue_compilation(context)) + { + const std::string error_message = + !context.error_message.empty() ? " Details: " + context.error_message : ""; + + set_error(make_error( + parser_error::e_parser, + token_t(), + "ERR011 - Internal compilation check failed." + error_message, + exprtk_error_location)); + + return true; + } + + return false; + } + + inline expression_node_ptr parse_expression(precedence_level precedence = e_level00) + { + if (halt_compilation_check()) + { + exprtk_debug(("halt_compilation_check() - parse_expression checkpoint 2\n")); + return error_node(); + } + + stack_limit_handler slh(*this); + + if (!slh) + { + return error_node(); + } + + expression_node_ptr expression = parse_branch(precedence); + + if (0 == expression) + { + return error_node(); + } + + if (token_is(token_t::e_eof,prsrhlpr_t::e_hold)) + { + return expression; + } + + bool break_loop = false; + + state_t current_state; + + for ( ; ; ) + { + current_state.reset(); + + switch (current_token().type) + { + case token_t::e_assign : current_state.set(e_level00, e_level00, details::e_assign, current_token()); break; + case token_t::e_addass : current_state.set(e_level00, e_level00, details::e_addass, current_token()); break; + case token_t::e_subass : current_state.set(e_level00, e_level00, details::e_subass, current_token()); break; + case token_t::e_mulass : current_state.set(e_level00, e_level00, details::e_mulass, current_token()); break; + case token_t::e_divass : current_state.set(e_level00, e_level00, details::e_divass, current_token()); break; + case token_t::e_modass : current_state.set(e_level00, e_level00, details::e_modass, current_token()); break; + case token_t::e_swap : current_state.set(e_level00, e_level00, details::e_swap , current_token()); break; + case token_t::e_lt : current_state.set(e_level05, e_level06, details::e_lt , current_token()); break; + case token_t::e_lte : current_state.set(e_level05, e_level06, details::e_lte , current_token()); break; + case token_t::e_eq : current_state.set(e_level05, e_level06, details::e_eq , current_token()); break; + case token_t::e_ne : current_state.set(e_level05, e_level06, details::e_ne , current_token()); break; + case token_t::e_gte : current_state.set(e_level05, e_level06, details::e_gte , current_token()); break; + case token_t::e_gt : current_state.set(e_level05, e_level06, details::e_gt , current_token()); break; + case token_t::e_add : current_state.set(e_level07, e_level08, details::e_add , current_token()); break; + case token_t::e_sub : current_state.set(e_level07, e_level08, details::e_sub , current_token()); break; + case token_t::e_div : current_state.set(e_level10, e_level11, details::e_div , current_token()); break; + case token_t::e_mul : current_state.set(e_level10, e_level11, details::e_mul , current_token()); break; + case token_t::e_mod : current_state.set(e_level10, e_level11, details::e_mod , current_token()); break; + case token_t::e_pow : current_state.set(e_level12, e_level12, details::e_pow , current_token()); break; + default : + if (token_t::e_symbol == current_token().type) + { + static const std::string s_and = "and" ; + static const std::string s_nand = "nand" ; + static const std::string s_or = "or" ; + static const std::string s_nor = "nor" ; + static const std::string s_xor = "xor" ; + static const std::string s_xnor = "xnor" ; + static const std::string s_in = "in" ; + static const std::string s_like = "like" ; + static const std::string s_ilike = "ilike"; + static const std::string s_and1 = "&" ; + static const std::string s_or1 = "|" ; + static const std::string s_not = "not" ; + + if (details::imatch(current_token().value,s_and)) + { + current_state.set(e_level03, e_level04, details::e_and, current_token()); + break; + } + else if (details::imatch(current_token().value,s_and1)) + { + #ifndef exprtk_disable_sc_andor + current_state.set(e_level03, e_level04, details::e_scand, current_token()); + #else + current_state.set(e_level03, e_level04, details::e_and, current_token()); + #endif + break; + } + else if (details::imatch(current_token().value,s_nand)) + { + current_state.set(e_level03, e_level04, details::e_nand, current_token()); + break; + } + else if (details::imatch(current_token().value,s_or)) + { + current_state.set(e_level01, e_level02, details::e_or, current_token()); + break; + } + else if (details::imatch(current_token().value,s_or1)) + { + #ifndef exprtk_disable_sc_andor + current_state.set(e_level01, e_level02, details::e_scor, current_token()); + #else + current_state.set(e_level01, e_level02, details::e_or, current_token()); + #endif + break; + } + else if (details::imatch(current_token().value,s_nor)) + { + current_state.set(e_level01, e_level02, details::e_nor, current_token()); + break; + } + else if (details::imatch(current_token().value,s_xor)) + { + current_state.set(e_level01, e_level02, details::e_xor, current_token()); + break; + } + else if (details::imatch(current_token().value,s_xnor)) + { + current_state.set(e_level01, e_level02, details::e_xnor, current_token()); + break; + } + else if (details::imatch(current_token().value,s_in)) + { + current_state.set(e_level04, e_level04, details::e_in, current_token()); + break; + } + else if (details::imatch(current_token().value,s_like)) + { + current_state.set(e_level04, e_level04, details::e_like, current_token()); + break; + } + else if (details::imatch(current_token().value,s_ilike)) + { + current_state.set(e_level04, e_level04, details::e_ilike, current_token()); + break; + } + else if (details::imatch(current_token().value,s_not)) + { + break; + } + } + + break_loop = true; + } + + if (break_loop) + { + parse_pending_string_rangesize(expression); + break; + } + else if (current_state.left < precedence) + break; + + const lexer::token prev_token = current_token(); + + next_token(); + + expression_node_ptr right_branch = error_node(); + expression_node_ptr new_expression = error_node(); + + if (is_invalid_logic_operation(current_state.operation)) + { + free_node(node_allocator_, expression); + + set_error(make_error( + parser_error::e_syntax, + prev_token, + "ERR012 - Invalid or disabled logic operation '" + details::to_str(current_state.operation) + "'", + exprtk_error_location)); + + return error_node(); + } + else if (is_invalid_arithmetic_operation(current_state.operation)) + { + free_node(node_allocator_, expression); + + set_error(make_error( + parser_error::e_syntax, + prev_token, + "ERR013 - Invalid or disabled arithmetic operation '" + details::to_str(current_state.operation) + "'", + exprtk_error_location)); + + return error_node(); + } + else if (is_invalid_inequality_operation(current_state.operation)) + { + free_node(node_allocator_, expression); + + set_error(make_error( + parser_error::e_syntax, + prev_token, + "ERR014 - Invalid inequality operation '" + details::to_str(current_state.operation) + "'", + exprtk_error_location)); + + return error_node(); + } + else if (is_invalid_assignment_operation(current_state.operation)) + { + free_node(node_allocator_, expression); + + set_error(make_error( + parser_error::e_syntax, + prev_token, + "ERR015 - Invalid or disabled assignment operation '" + details::to_str(current_state.operation) + "'", + exprtk_error_location)); + + return error_node(); + } + + if (0 != (right_branch = parse_expression(current_state.right))) + { + if ( + details::is_return_node(expression ) || + details::is_return_node(right_branch) + ) + { + free_node(node_allocator_, expression ); + free_node(node_allocator_, right_branch); + + set_error(make_error( + parser_error::e_syntax, + prev_token, + "ERR016 - Return statements cannot be part of sub-expressions", + exprtk_error_location)); + + return error_node(); + } + + push_current_state(current_state); + + new_expression = expression_generator_ + ( + current_state.operation, + expression, + right_branch + ); + + pop_current_state(); + } + + if (0 == new_expression) + { + if (error_list_.empty()) + { + set_error(make_error( + parser_error::e_syntax, + prev_token, + !synthesis_error_.empty() ? + synthesis_error_ : + "ERR017 - General parsing error at token: '" + prev_token.value + "'", + exprtk_error_location)); + } + + free_node(node_allocator_, expression ); + free_node(node_allocator_, right_branch); + + return error_node(); + } + else + { + if ( + token_is(token_t::e_ternary,prsrhlpr_t::e_hold) && + (e_level00 == precedence) + ) + { + expression = parse_ternary_conditional_statement(new_expression); + } + else + expression = new_expression; + + parse_pending_string_rangesize(expression); + } + } + + if ((0 != expression) && (expression->node_depth() > settings_.max_node_depth_)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR018 - Expression depth of " + details::to_str(static_cast(expression->node_depth())) + + " exceeds maximum allowed expression depth of " + details::to_str(static_cast(settings_.max_node_depth_)), + exprtk_error_location)); + + free_node(node_allocator_, expression); + + return error_node(); + } + else if ( + !settings_.commutative_check_enabled() && + !details::is_logic_opr(current_token().value) && + (current_state.operation == details::e_default) && + ( + current_token().type == token_t::e_symbol || + current_token().type == token_t::e_number || + current_token().type == token_t::e_string + ) + ) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR019 - Invalid syntax '" + current_token().value + "' possible missing operator or context", + exprtk_error_location)); + + free_node(node_allocator_, expression); + + return error_node(); + } + + return expression; + } + + bool simplify_unary_negation_branch(expression_node_ptr& node) + { + { + typedef details::unary_branch_node > ubn_t; + ubn_t* n = dynamic_cast(node); + + if (n) + { + expression_node_ptr un_r = n->branch(0); + n->release(); + free_node(node_allocator_, node); + node = un_r; + + return true; + } + } + + { + typedef details::unary_variable_node > uvn_t; + + uvn_t* n = dynamic_cast(node); + + if (n) + { + const T& v = n->v(); + expression_node_ptr return_node = error_node(); + + if ( + (0 != (return_node = symtab_store_.get_variable(v))) || + (0 != (return_node = sem_ .get_variable(v))) + ) + { + free_node(node_allocator_, node); + node = return_node; + + return true; + } + else + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR020 - Failed to find variable node in symbol table", + exprtk_error_location)); + + free_node(node_allocator_, node); + + return false; + } + } + } + + return false; + } + + static inline expression_node_ptr error_node() + { + return reinterpret_cast(0); + } + + struct scoped_expression_delete + { + scoped_expression_delete(parser& pr, expression_node_ptr& expression) + : delete_ptr(true) + , parser_(pr) + , expression_(expression) + {} + + ~scoped_expression_delete() + { + if (delete_ptr) + { + free_node(parser_.node_allocator_, expression_); + } + } + + bool delete_ptr; + parser& parser_; + expression_node_ptr& expression_; + + private: + + scoped_expression_delete(const scoped_expression_delete&) exprtk_delete; + scoped_expression_delete& operator=(const scoped_expression_delete&) exprtk_delete; + }; + + template + struct scoped_delete + { + typedef Type* ptr_t; + + scoped_delete(parser& pr, ptr_t& p) + : delete_ptr(true) + , parser_(pr) + , p_(&p) + {} + + scoped_delete(parser& pr, ptr_t (&p)[N]) + : delete_ptr(true) + , parser_(pr) + , p_(&p[0]) + {} + + ~scoped_delete() + { + if (delete_ptr) + { + for (std::size_t i = 0; i < N; ++i) + { + free_node(parser_.node_allocator_, p_[i]); + } + } + } + + bool delete_ptr; + parser& parser_; + ptr_t* p_; + + private: + + scoped_delete(const scoped_delete&) exprtk_delete; + scoped_delete& operator=(const scoped_delete&) exprtk_delete; + }; + + template + struct scoped_deq_delete + { + typedef Type* ptr_t; + + scoped_deq_delete(parser& pr, std::deque& deq) + : delete_ptr(true) + , parser_(pr) + , deq_(deq) + {} + + ~scoped_deq_delete() + { + if (delete_ptr && !deq_.empty()) + { + for (std::size_t i = 0; i < deq_.size(); ++i) + { + exprtk_debug(("~scoped_deq_delete() - deleting node: %p\n", reinterpret_cast(deq_[i]))); + free_node(parser_.node_allocator_,deq_[i]); + } + + deq_.clear(); + } + } + + bool delete_ptr; + parser& parser_; + std::deque& deq_; + + private: + + scoped_deq_delete(const scoped_deq_delete&) exprtk_delete; + scoped_deq_delete& operator=(const scoped_deq_delete&) exprtk_delete; + }; + + template + struct scoped_vec_delete + { + typedef Type* ptr_t; + + scoped_vec_delete(parser& pr, std::vector& vec) + : delete_ptr(true) + , parser_(pr) + , vec_(vec) + {} + + ~scoped_vec_delete() + { + if (delete_ptr && !vec_.empty()) + { + for (std::size_t i = 0; i < vec_.size(); ++i) + { + exprtk_debug(("~scoped_vec_delete() - deleting node: %p\n", reinterpret_cast(vec_[i]))); + free_node(parser_.node_allocator_,vec_[i]); + } + + vec_.clear(); + } + } + + ptr_t operator[](const std::size_t index) + { + return vec_[index]; + } + + bool delete_ptr; + parser& parser_; + std::vector& vec_; + + private: + + scoped_vec_delete(const scoped_vec_delete&) exprtk_delete; + scoped_vec_delete& operator=(const scoped_vec_delete&) exprtk_delete; + }; + + struct scoped_bool_negator + { + explicit scoped_bool_negator(bool& bb) + : b(bb) + { b = !b; } + + ~scoped_bool_negator() + { b = !b; } + + bool& b; + }; + + struct scoped_bool_or_restorer + { + explicit scoped_bool_or_restorer(bool& bb) + : b(bb) + , original_value_(bb) + {} + + ~scoped_bool_or_restorer() + { + b = b || original_value_; + } + + bool& b; + bool original_value_; + }; + + struct scoped_inc_dec + { + explicit scoped_inc_dec(std::size_t& v) + : v_(v) + { ++v_; } + + ~scoped_inc_dec() + { + assert(v_ > 0); + --v_; + } + + std::size_t& v_; + }; + + inline expression_node_ptr parse_function_invocation(ifunction* function, const std::string& function_name) + { + expression_node_ptr func_node = reinterpret_cast(0); + + switch (function->param_count) + { + case 0 : func_node = parse_function_call_0 (function,function_name); break; + case 1 : func_node = parse_function_call< 1>(function,function_name); break; + case 2 : func_node = parse_function_call< 2>(function,function_name); break; + case 3 : func_node = parse_function_call< 3>(function,function_name); break; + case 4 : func_node = parse_function_call< 4>(function,function_name); break; + case 5 : func_node = parse_function_call< 5>(function,function_name); break; + case 6 : func_node = parse_function_call< 6>(function,function_name); break; + case 7 : func_node = parse_function_call< 7>(function,function_name); break; + case 8 : func_node = parse_function_call< 8>(function,function_name); break; + case 9 : func_node = parse_function_call< 9>(function,function_name); break; + case 10 : func_node = parse_function_call<10>(function,function_name); break; + case 11 : func_node = parse_function_call<11>(function,function_name); break; + case 12 : func_node = parse_function_call<12>(function,function_name); break; + case 13 : func_node = parse_function_call<13>(function,function_name); break; + case 14 : func_node = parse_function_call<14>(function,function_name); break; + case 15 : func_node = parse_function_call<15>(function,function_name); break; + case 16 : func_node = parse_function_call<16>(function,function_name); break; + case 17 : func_node = parse_function_call<17>(function,function_name); break; + case 18 : func_node = parse_function_call<18>(function,function_name); break; + case 19 : func_node = parse_function_call<19>(function,function_name); break; + case 20 : func_node = parse_function_call<20>(function,function_name); break; + default : { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR021 - Invalid number of parameters for function: '" + function_name + "'", + exprtk_error_location)); + + return error_node(); + } + } + + if (func_node) + return func_node; + else + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR022 - Failed to generate call to function: '" + function_name + "'", + exprtk_error_location)); + + return error_node(); + } + } + + template + inline expression_node_ptr parse_function_call(ifunction* function, const std::string& function_name) + { + #ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable: 4127) + #endif + if (0 == NumberofParameters) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR023 - Expecting ifunction '" + function_name + "' to have non-zero parameter count", + exprtk_error_location)); + + return error_node(); + } + #ifdef _MSC_VER + #pragma warning(pop) + #endif + + expression_node_ptr branch[NumberofParameters]; + expression_node_ptr result = error_node(); + + std::fill_n(branch, NumberofParameters, reinterpret_cast(0)); + + scoped_delete sd((*this),branch); + + next_token(); + + if (!token_is(token_t::e_lbracket)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR024 - Expecting argument list for function: '" + function_name + "'", + exprtk_error_location)); + + return error_node(); + } + + for (int i = 0; i < static_cast(NumberofParameters); ++i) + { + branch[i] = parse_expression(); + + if (0 == branch[i]) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR025 - Failed to parse argument " + details::to_str(i) + " for function: '" + function_name + "'", + exprtk_error_location)); + + return error_node(); + } + else if (i < static_cast(NumberofParameters - 1)) + { + if (!token_is(token_t::e_comma)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR026 - Invalid number of arguments for function: '" + function_name + "'", + exprtk_error_location)); + + return error_node(); + } + } + } + + if (!token_is(token_t::e_rbracket)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR027 - Invalid number of arguments for function: '" + function_name + "'", + exprtk_error_location)); + + return error_node(); + } + else + result = expression_generator_.function(function,branch); + + sd.delete_ptr = (0 == result); + + return result; + } + + inline expression_node_ptr parse_function_call_0(ifunction* function, const std::string& function_name) + { + expression_node_ptr result = expression_generator_.function(function); + + state_.side_effect_present = function->has_side_effects(); + + next_token(); + + if ( + token_is(token_t::e_lbracket) && + !token_is(token_t::e_rbracket) + ) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR028 - Expecting '()' to proceed call to function: '" + function_name + "'", + exprtk_error_location)); + + free_node(node_allocator_, result); + + return error_node(); + } + else + return result; + } + + template + inline std::size_t parse_base_function_call(expression_node_ptr (¶m_list)[MaxNumberofParameters], const std::string& function_name = "") + { + std::fill_n(param_list, MaxNumberofParameters, reinterpret_cast(0)); + + scoped_delete sd((*this),param_list); + + next_token(); + + if (!token_is(token_t::e_lbracket)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR029 - Expected a '(' at start of function call to '" + function_name + + "', instead got: '" + current_token().value + "'", + exprtk_error_location)); + + return 0; + } + + if (token_is(token_t::e_rbracket, e_hold)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR030 - Expected at least one input parameter for function call '" + function_name + "'", + exprtk_error_location)); + + return 0; + } + + std::size_t param_index = 0; + + for (; param_index < MaxNumberofParameters; ++param_index) + { + param_list[param_index] = parse_expression(); + + if (0 == param_list[param_index]) + return 0; + else if (token_is(token_t::e_rbracket)) + { + sd.delete_ptr = false; + break; + } + else if (token_is(token_t::e_comma)) + continue; + else + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR031 - Expected a ',' between function input parameters, instead got: '" + current_token().value + "'", + exprtk_error_location)); + + return 0; + } + } + + if (sd.delete_ptr) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR032 - Invalid number of input parameters passed to function '" + function_name + "'", + exprtk_error_location)); + + return 0; + } + + return (param_index + 1); + } + + inline expression_node_ptr parse_base_operation() + { + typedef std::pair map_range_t; + + const std::string operation_name = current_token().value; + const token_t diagnostic_token = current_token(); + + map_range_t itr_range = base_ops_map_.equal_range(operation_name); + + if (0 == std::distance(itr_range.first,itr_range.second)) + { + set_error(make_error( + parser_error::e_syntax, + diagnostic_token, + "ERR033 - No entry found for base operation: " + operation_name, + exprtk_error_location)); + + return error_node(); + } + + static const std::size_t MaxNumberofParameters = 4; + expression_node_ptr param_list[MaxNumberofParameters] = {0}; + + const std::size_t parameter_count = parse_base_function_call(param_list, operation_name); + + if ((parameter_count > 0) && (parameter_count <= MaxNumberofParameters)) + { + for (base_ops_map_t::iterator itr = itr_range.first; itr != itr_range.second; ++itr) + { + const details::base_operation_t& operation = itr->second; + + if (operation.num_params == parameter_count) + { + switch (parameter_count) + { + #define base_opr_case(N) \ + case N : { \ + expression_node_ptr pl##N[N] = {0}; \ + std::copy(param_list, param_list + N, pl##N); \ + lodge_symbol(operation_name, e_st_function); \ + return expression_generator_(operation.type, pl##N); \ + } \ + + base_opr_case(1) + base_opr_case(2) + base_opr_case(3) + base_opr_case(4) + #undef base_opr_case + } + } + } + } + + for (std::size_t i = 0; i < MaxNumberofParameters; ++i) + { + free_node(node_allocator_, param_list[i]); + } + + set_error(make_error( + parser_error::e_syntax, + diagnostic_token, + "ERR034 - Invalid number of input parameters for call to function: '" + operation_name + "'", + exprtk_error_location)); + + return error_node(); + } + + inline expression_node_ptr parse_conditional_statement_01(expression_node_ptr condition) + { + // Parse: [if][(][condition][,][consequent][,][alternative][)] + + expression_node_ptr consequent = error_node(); + expression_node_ptr alternative = error_node(); + + bool result = true; + + if (!token_is(token_t::e_comma)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR035 - Expected ',' between if-statement condition and consequent", + exprtk_error_location)); + + result = false; + } + else if (0 == (consequent = parse_expression())) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR036 - Failed to parse consequent for if-statement", + exprtk_error_location)); + + result = false; + } + else if (!token_is(token_t::e_comma)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR037 - Expected ',' between if-statement consequent and alternative", + exprtk_error_location)); + + result = false; + } + else if (0 == (alternative = parse_expression())) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR038 - Failed to parse alternative for if-statement", + exprtk_error_location)); + + result = false; + } + else if (!token_is(token_t::e_rbracket)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR039 - Expected ')' at the end of if-statement", + exprtk_error_location)); + + result = false; + } + + #ifndef exprtk_disable_string_capabilities + if (result) + { + const bool consq_is_str = is_generally_string_node(consequent ); + const bool alter_is_str = is_generally_string_node(alternative); + + if (consq_is_str || alter_is_str) + { + if (consq_is_str && alter_is_str) + { + expression_node_ptr result_node = + expression_generator_ + .conditional_string(condition, consequent, alternative); + + if (result_node && result_node->valid()) + { + return result_node; + } + + set_error(make_error( + parser_error::e_synthesis, + current_token(), + "ERR040 - Failed to synthesize node: conditional_string", + exprtk_error_location)); + + free_node(node_allocator_, result_node); + return error_node(); + } + + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR041 - Return types of if-statement differ: string/non-string", + exprtk_error_location)); + + result = false; + } + } + #endif + + if (result) + { + const bool consq_is_vec = is_ivector_node(consequent ); + const bool alter_is_vec = is_ivector_node(alternative); + + if (consq_is_vec || alter_is_vec) + { + if (consq_is_vec && alter_is_vec) + { + return expression_generator_ + .conditional_vector(condition, consequent, alternative); + } + + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR042 - Return types of if-statement differ: vector/non-vector", + exprtk_error_location)); + + result = false; + } + } + + if (!result) + { + free_node(node_allocator_, condition ); + free_node(node_allocator_, consequent ); + free_node(node_allocator_, alternative); + + return error_node(); + } + else + return expression_generator_ + .conditional(condition, consequent, alternative); + } + + inline expression_node_ptr parse_conditional_statement_02(expression_node_ptr condition) + { + expression_node_ptr consequent = error_node(); + expression_node_ptr alternative = error_node(); + + bool result = true; + + if (token_is(token_t::e_lcrlbracket,prsrhlpr_t::e_hold)) + { + if (0 == (consequent = parse_multi_sequence("if-statement-01"))) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR043 - Failed to parse body of consequent for if-statement", + exprtk_error_location)); + + result = false; + } + else if + ( + !settings_.commutative_check_enabled() && + !token_is("else",prsrhlpr_t::e_hold) && + !token_is_loop(prsrhlpr_t::e_hold) && + !token_is_arithmetic_opr(prsrhlpr_t::e_hold) && + !token_is_right_bracket (prsrhlpr_t::e_hold) && + !token_is_ineq_opr (prsrhlpr_t::e_hold) && + !token_is(token_t::e_ternary,prsrhlpr_t::e_hold) && + !token_is(token_t::e_eof ,prsrhlpr_t::e_hold) + ) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR044 - Expected ';' at the end of the consequent for if-statement (1)", + exprtk_error_location)); + + result = false; + } + } + else + { + if ( + settings_.commutative_check_enabled() && + token_is(token_t::e_mul,prsrhlpr_t::e_hold) + ) + { + next_token(); + } + + if (0 != (consequent = parse_expression())) + { + if (!token_is(token_t::e_eof, prsrhlpr_t::e_hold)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR045 - Expected ';' at the end of the consequent for if-statement (2)", + exprtk_error_location)); + + result = false; + } + } + else + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR046 - Failed to parse body of consequent for if-statement", + exprtk_error_location)); + + result = false; + } + } + + if (result) + { + if ( + details::imatch(current_token().value,"else") || + (token_is(token_t::e_eof, prsrhlpr_t::e_hold) && peek_token_is("else")) + ) + { + next_token(); + + if (details::imatch(current_token().value,"else")) + { + next_token(); + } + + if (token_is(token_t::e_lcrlbracket,prsrhlpr_t::e_hold)) + { + if (0 == (alternative = parse_multi_sequence("else-statement-01"))) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR047 - Failed to parse body of the 'else' for if-statement", + exprtk_error_location)); + + result = false; + } + } + else if (details::imatch(current_token().value,"if")) + { + if (0 == (alternative = parse_conditional_statement())) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR048 - Failed to parse body of if-else statement", + exprtk_error_location)); + + result = false; + } + } + else if (0 != (alternative = parse_expression())) + { + if ( + !token_is(token_t::e_ternary , prsrhlpr_t::e_hold) && + !token_is(token_t::e_rcrlbracket, prsrhlpr_t::e_hold) && + !token_is(token_t::e_eof) + ) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR049 - Expected ';' at the end of the 'else-if' for the if-statement", + exprtk_error_location)); + + result = false; + } + } + else + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR050 - Failed to parse body of the 'else' for if-statement", + exprtk_error_location)); + + result = false; + } + } + } + + #ifndef exprtk_disable_string_capabilities + if (result) + { + const bool consq_is_str = is_generally_string_node(consequent ); + const bool alter_is_str = is_generally_string_node(alternative); + + if (consq_is_str || alter_is_str) + { + if (consq_is_str && alter_is_str) + { + return expression_generator_ + .conditional_string(condition, consequent, alternative); + } + + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR051 - Return types of if-statement differ: string/non-string", + exprtk_error_location)); + + result = false; + } + } + #endif + + if (result) + { + const bool consq_is_vec = is_ivector_node(consequent ); + const bool alter_is_vec = is_ivector_node(alternative); + + if (consq_is_vec || alter_is_vec) + { + if (consq_is_vec && alter_is_vec) + { + return expression_generator_ + .conditional_vector(condition, consequent, alternative); + } + + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR052 - Return types of if-statement differ: vector/non-vector", + exprtk_error_location)); + + result = false; + } + } + + if (!result) + { + free_node(node_allocator_, condition ); + free_node(node_allocator_, consequent ); + free_node(node_allocator_, alternative); + + return error_node(); + } + else + return expression_generator_ + .conditional(condition, consequent, alternative); + } + + inline expression_node_ptr parse_conditional_statement() + { + expression_node_ptr condition = error_node(); + + next_token(); + + if (!token_is(token_t::e_lbracket)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR053 - Expected '(' at start of if-statement, instead got: '" + current_token().value + "'", + exprtk_error_location)); + + return error_node(); + } + else if (0 == (condition = parse_expression())) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR054 - Failed to parse condition for if-statement", + exprtk_error_location)); + + return error_node(); + } + else if (token_is(token_t::e_comma,prsrhlpr_t::e_hold)) + { + // if (x,y,z) + return parse_conditional_statement_01(condition); + } + else if (token_is(token_t::e_rbracket)) + { + /* + 00. if (x) y; + 01. if (x) y; else z; + 02. if (x) y; else {z0; ... zn;} + 03. if (x) y; else if (z) w; + 04. if (x) y; else if (z) w; else u; + 05. if (x) y; else if (z) w; else {u0; ... un;} + 06. if (x) y; else if (z) {w0; ... wn;} + 07. if (x) {y0; ... yn;} + 08. if (x) {y0; ... yn;} else z; + 09. if (x) {y0; ... yn;} else {z0; ... zn;}; + 10. if (x) {y0; ... yn;} else if (z) w; + 11. if (x) {y0; ... yn;} else if (z) w; else u; + 12. if (x) {y0; ... nex;} else if (z) w; else {u0 ... un;} + 13. if (x) {y0; ... yn;} else if (z) {w0; ... wn;} + */ + return parse_conditional_statement_02(condition); + } + + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR055 - Invalid if-statement", + exprtk_error_location)); + + free_node(node_allocator_, condition); + + return error_node(); + } + + inline expression_node_ptr parse_ternary_conditional_statement(expression_node_ptr condition) + { + // Parse: [condition][?][consequent][:][alternative] + expression_node_ptr consequent = error_node(); + expression_node_ptr alternative = error_node(); + + bool result = true; + + if (0 == condition) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR056 - Encountered invalid condition branch for ternary if-statement", + exprtk_error_location)); + + return error_node(); + } + else if (!token_is(token_t::e_ternary)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR057 - Expected '?' after condition of ternary if-statement", + exprtk_error_location)); + + result = false; + } + else if (0 == (consequent = parse_expression())) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR058 - Failed to parse consequent for ternary if-statement", + exprtk_error_location)); + + result = false; + } + else if (!token_is(token_t::e_colon)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR059 - Expected ':' between ternary if-statement consequent and alternative", + exprtk_error_location)); + + result = false; + } + else if (0 == (alternative = parse_expression())) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR060 - Failed to parse alternative for ternary if-statement", + exprtk_error_location)); + + result = false; + } + + #ifndef exprtk_disable_string_capabilities + if (result) + { + const bool consq_is_str = is_generally_string_node(consequent ); + const bool alter_is_str = is_generally_string_node(alternative); + + if (consq_is_str || alter_is_str) + { + if (consq_is_str && alter_is_str) + { + return expression_generator_ + .conditional_string(condition, consequent, alternative); + } + + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR061 - Return types of ternary differ: string/non-string", + exprtk_error_location)); + + result = false; + } + } + #endif + + if (result) + { + const bool consq_is_vec = is_ivector_node(consequent ); + const bool alter_is_vec = is_ivector_node(alternative); + + if (consq_is_vec || alter_is_vec) + { + if (consq_is_vec && alter_is_vec) + { + return expression_generator_ + .conditional_vector(condition, consequent, alternative); + } + + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR062 - Return types of ternary differ: vector/non-vector", + exprtk_error_location)); + + result = false; + } + } + + if (!result) + { + free_node(node_allocator_, condition ); + free_node(node_allocator_, consequent ); + free_node(node_allocator_, alternative); + + return error_node(); + } + else + return expression_generator_ + .conditional(condition, consequent, alternative); + } + + inline expression_node_ptr parse_not_statement() + { + if (settings_.logic_disabled("not")) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR063 - Invalid or disabled logic operation 'not'", + exprtk_error_location)); + + return error_node(); + } + + return parse_base_operation(); + } + + void handle_brkcnt_scope_exit() + { + assert(!brkcnt_list_.empty()); + brkcnt_list_.pop_front(); + } + + inline expression_node_ptr parse_while_loop() + { + // Parse: [while][(][test expr][)][{][expression][}] + expression_node_ptr condition = error_node(); + expression_node_ptr branch = error_node(); + expression_node_ptr result_node = error_node(); + + bool result = true; + + next_token(); + + if (!token_is(token_t::e_lbracket)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR064 - Expected '(' at start of while-loop condition statement", + exprtk_error_location)); + + return error_node(); + } + else if (0 == (condition = parse_expression())) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR065 - Failed to parse condition for while-loop", + exprtk_error_location)); + + return error_node(); + } + else if (!token_is(token_t::e_rbracket)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR066 - Expected ')' at end of while-loop condition statement", + exprtk_error_location)); + + result = false; + } + + brkcnt_list_.push_front(false); + + if (result) + { + scoped_inc_dec sid(state_.parsing_loop_stmt_count); + + if (0 == (branch = parse_multi_sequence("while-loop", true))) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR067 - Failed to parse body of while-loop")); + result = false; + } + else if (0 == (result_node = expression_generator_.while_loop(condition, + branch, + brkcnt_list_.front()))) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR068 - Failed to synthesize while-loop", + exprtk_error_location)); + + result = false; + } + } + + handle_brkcnt_scope_exit(); + + if (!result) + { + free_node(node_allocator_, branch ); + free_node(node_allocator_, condition ); + free_node(node_allocator_, result_node); + + return error_node(); + } + + if (result_node && result_node->valid()) + { + return result_node; + } + + set_error(make_error( + parser_error::e_synthesis, + current_token(), + "ERR069 - Failed to synthesize 'valid' while-loop", + exprtk_error_location)); + + free_node(node_allocator_, result_node); + + return error_node(); + } + + inline expression_node_ptr parse_repeat_until_loop() + { + // Parse: [repeat][{][expression][}][until][(][test expr][)] + expression_node_ptr condition = error_node(); + expression_node_ptr branch = error_node(); + next_token(); + + std::vector arg_list; + std::vector side_effect_list; + + scoped_vec_delete svd((*this), arg_list); + + brkcnt_list_.push_front(false); + + if (details::imatch(current_token().value,"until")) + { + next_token(); + branch = node_allocator_.allocate >(); + } + else + { + const token_t::token_type separator = token_t::e_eof; + + scope_handler sh(*this); + + scoped_bool_or_restorer sbr(state_.side_effect_present); + + scoped_inc_dec sid(state_.parsing_loop_stmt_count); + + for ( ; ; ) + { + state_.side_effect_present = false; + + expression_node_ptr arg = parse_expression(); + + if (0 == arg) + return error_node(); + else + { + arg_list.push_back(arg); + side_effect_list.push_back(state_.side_effect_present); + } + + if (details::imatch(current_token().value,"until")) + { + next_token(); + break; + } + + const bool is_next_until = peek_token_is(token_t::e_symbol) && + peek_token_is("until"); + + if (!token_is(separator) && is_next_until) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR070 - Expected '" + token_t::to_str(separator) + "' in body of repeat until loop", + exprtk_error_location)); + + return error_node(); + } + + if (details::imatch(current_token().value,"until")) + { + next_token(); + break; + } + } + + branch = simplify(arg_list,side_effect_list); + + svd.delete_ptr = (0 == branch); + + if (svd.delete_ptr) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR071 - Failed to parse body of repeat until loop", + exprtk_error_location)); + + return error_node(); + } + } + + if (!token_is(token_t::e_lbracket)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR072 - Expected '(' before condition statement of repeat until loop", + exprtk_error_location)); + + free_node(node_allocator_, branch); + return error_node(); + } + else if (0 == (condition = parse_expression())) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR073 - Failed to parse condition for repeat until loop", + exprtk_error_location)); + + free_node(node_allocator_, branch); + return error_node(); + } + else if (!token_is(token_t::e_rbracket)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR074 - Expected ')' after condition of repeat until loop", + exprtk_error_location)); + + free_node(node_allocator_, branch ); + free_node(node_allocator_, condition); + + return error_node(); + } + + expression_node_ptr result_node = + expression_generator_ + .repeat_until_loop( + condition, + branch, + brkcnt_list_.front()); + + if (0 == result_node) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR075 - Failed to synthesize repeat until loop", + exprtk_error_location)); + + free_node(node_allocator_, condition); + + return error_node(); + } + + handle_brkcnt_scope_exit(); + + if (result_node && result_node->valid()) + { + return result_node; + } + + set_error(make_error( + parser_error::e_synthesis, + current_token(), + "ERR076 - Failed to synthesize 'valid' repeat until loop", + exprtk_error_location)); + + free_node(node_allocator_, result_node); + + return error_node(); + } + + inline expression_node_ptr parse_for_loop() + { + expression_node_ptr initialiser = error_node(); + expression_node_ptr condition = error_node(); + expression_node_ptr incrementor = error_node(); + expression_node_ptr loop_body = error_node(); + + scope_element* se = 0; + bool result = true; + + next_token(); + + scope_handler sh(*this); + + if (!token_is(token_t::e_lbracket)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR077 - Expected '(' at start of for-loop", + exprtk_error_location)); + + return error_node(); + } + + if (!token_is(token_t::e_eof)) + { + if ( + !token_is(token_t::e_symbol,prsrhlpr_t::e_hold) && + details::imatch(current_token().value,"var") + ) + { + next_token(); + + if (!token_is(token_t::e_symbol,prsrhlpr_t::e_hold)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR078 - Expected a variable at the start of initialiser section of for-loop", + exprtk_error_location)); + + return error_node(); + } + else if (!peek_token_is(token_t::e_assign)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR079 - Expected variable assignment of initialiser section of for-loop", + exprtk_error_location)); + + return error_node(); + } + + const std::string loop_counter_symbol = current_token().value; + + se = &sem_.get_element(loop_counter_symbol); + + if ((se->name == loop_counter_symbol) && se->active) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR080 - For-loop variable '" + loop_counter_symbol+ "' is being shadowed by a previous declaration", + exprtk_error_location)); + + return error_node(); + } + else if (!symtab_store_.is_variable(loop_counter_symbol)) + { + if ( + !se->active && + (se->name == loop_counter_symbol) && + (se->type == scope_element::e_variable) + ) + { + se->active = true; + se->ref_count++; + } + else + { + scope_element nse; + nse.name = loop_counter_symbol; + nse.active = true; + nse.ref_count = 1; + nse.type = scope_element::e_variable; + nse.depth = state_.scope_depth; + nse.data = new T(T(0)); + nse.var_node = node_allocator_.allocate(*reinterpret_cast(nse.data)); + + if (!sem_.add_element(nse)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR081 - Failed to add new local variable '" + loop_counter_symbol + "' to SEM", + exprtk_error_location)); + + sem_.free_element(nse); + + result = false; + } + else + { + exprtk_debug(("parse_for_loop() - INFO - Added new local variable: %s\n", nse.name.c_str())); + + state_.activate_side_effect("parse_for_loop()"); + } + } + } + } + + if (0 == (initialiser = parse_expression())) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR082 - Failed to parse initialiser of for-loop", + exprtk_error_location)); + + result = false; + } + else if (!token_is(token_t::e_eof)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR083 - Expected ';' after initialiser of for-loop", + exprtk_error_location)); + + result = false; + } + } + + if (!token_is(token_t::e_eof)) + { + if (0 == (condition = parse_expression())) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR084 - Failed to parse condition of for-loop", + exprtk_error_location)); + + result = false; + } + else if (!token_is(token_t::e_eof)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR085 - Expected ';' after condition section of for-loop", + exprtk_error_location)); + + result = false; + } + } + + if (!token_is(token_t::e_rbracket)) + { + if (0 == (incrementor = parse_expression())) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR086 - Failed to parse incrementor of for-loop", + exprtk_error_location)); + + result = false; + } + else if (!token_is(token_t::e_rbracket)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR087 - Expected ')' after incrementor section of for-loop", + exprtk_error_location)); + + result = false; + } + } + + if (result) + { + brkcnt_list_.push_front(false); + + scoped_inc_dec sid(state_.parsing_loop_stmt_count); + + if (0 == (loop_body = parse_multi_sequence("for-loop", true))) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR088 - Failed to parse body of for-loop", + exprtk_error_location)); + + result = false; + } + } + + if (!result) + { + if (se) + { + se->ref_count--; + } + + free_node(node_allocator_, initialiser); + free_node(node_allocator_, condition ); + free_node(node_allocator_, incrementor); + free_node(node_allocator_, loop_body ); + return error_node(); + } + + expression_node_ptr result_node = + expression_generator_.for_loop(initialiser, + condition, + incrementor, + loop_body, + brkcnt_list_.front()); + handle_brkcnt_scope_exit(); + + if (result_node && result_node->valid()) + { + return result_node; + } + + set_error(make_error( + parser_error::e_synthesis, + current_token(), + "ERR089 - Failed to synthesize 'valid' for-loop", + exprtk_error_location)); + + free_node(node_allocator_, result_node); + + return error_node(); + } + + inline expression_node_ptr parse_switch_statement() + { + std::vector arg_list; + + if (!details::imatch(current_token().value,"switch")) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR090 - Expected keyword 'switch'", + exprtk_error_location)); + + return error_node(); + } + + scoped_vec_delete svd((*this), arg_list); + + next_token(); + + if (!token_is(token_t::e_lcrlbracket)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR091 - Expected '{' for call to switch statement", + exprtk_error_location)); + + return error_node(); + } + + expression_node_ptr default_statement = error_node(); + + scoped_expression_delete defstmt_delete((*this), default_statement); + + for ( ; ; ) + { + if (details::imatch("case",current_token().value)) + { + next_token(); + + expression_node_ptr condition = parse_expression(); + + if (0 == condition) + return error_node(); + else if (!token_is(token_t::e_colon)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR092 - Expected ':' for case of switch statement", + exprtk_error_location)); + + free_node(node_allocator_, condition); + + return error_node(); + } + + expression_node_ptr consequent = + (token_is(token_t::e_lcrlbracket,prsrhlpr_t::e_hold)) ? + parse_multi_sequence("switch-consequent") : + parse_expression(); + + if (0 == consequent) + { + free_node(node_allocator_, condition); + + return error_node(); + } + else if (!token_is(token_t::e_eof)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR093 - Expected ';' at end of case for switch statement", + exprtk_error_location)); + + free_node(node_allocator_, condition ); + free_node(node_allocator_, consequent); + + return error_node(); + } + + // Can we optimise away the case statement? + if (is_constant_node(condition) && is_false(condition)) + { + free_node(node_allocator_, condition ); + free_node(node_allocator_, consequent); + } + else + { + arg_list.push_back(condition ); + arg_list.push_back(consequent); + } + + } + else if (details::imatch("default",current_token().value)) + { + if (0 != default_statement) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR094 - Multiple default cases for switch statement", + exprtk_error_location)); + + return error_node(); + } + + next_token(); + + if (!token_is(token_t::e_colon)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR095 - Expected ':' for default of switch statement", + exprtk_error_location)); + + return error_node(); + } + + default_statement = + (token_is(token_t::e_lcrlbracket,prsrhlpr_t::e_hold)) ? + parse_multi_sequence("switch-default"): + parse_expression(); + + if (0 == default_statement) + return error_node(); + else if (!token_is(token_t::e_eof)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR096 - Expected ';' at end of default for switch statement", + exprtk_error_location)); + + return error_node(); + } + } + else if (token_is(token_t::e_rcrlbracket)) + break; + else + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR097 - Expected '}' at end of switch statement", + exprtk_error_location)); + + return error_node(); + } + } + + const bool default_statement_present = (0 != default_statement); + + if (default_statement_present) + { + arg_list.push_back(default_statement); + } + else + { + arg_list.push_back(node_allocator_.allocate_c(std::numeric_limits::quiet_NaN())); + } + + expression_node_ptr result = expression_generator_.switch_statement(arg_list, (0 != default_statement)); + + svd.delete_ptr = (0 == result); + defstmt_delete.delete_ptr = (0 == result); + + return result; + } + + inline expression_node_ptr parse_multi_switch_statement() + { + std::vector arg_list; + + if (!details::imatch(current_token().value,"[*]")) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR098 - Expected token '[*]'", + exprtk_error_location)); + + return error_node(); + } + + scoped_vec_delete svd((*this), arg_list); + + next_token(); + + if (!token_is(token_t::e_lcrlbracket)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR099 - Expected '{' for call to [*] statement", + exprtk_error_location)); + + return error_node(); + } + + for ( ; ; ) + { + if (!details::imatch("case",current_token().value)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR100 - Expected a 'case' statement for multi-switch", + exprtk_error_location)); + + return error_node(); + } + + next_token(); + + expression_node_ptr condition = parse_expression(); + + if (0 == condition) + return error_node(); + + if (!token_is(token_t::e_colon)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR101 - Expected ':' for case of [*] statement", + exprtk_error_location)); + + return error_node(); + } + + expression_node_ptr consequent = + (token_is(token_t::e_lcrlbracket,prsrhlpr_t::e_hold)) ? + parse_multi_sequence("multi-switch-consequent") : + parse_expression(); + + if (0 == consequent) + return error_node(); + + if (!token_is(token_t::e_eof)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR102 - Expected ';' at end of case for [*] statement", + exprtk_error_location)); + + return error_node(); + } + + // Can we optimise away the case statement? + if (is_constant_node(condition) && is_false(condition)) + { + free_node(node_allocator_, condition ); + free_node(node_allocator_, consequent); + } + else + { + arg_list.push_back(condition ); + arg_list.push_back(consequent); + } + + if (token_is(token_t::e_rcrlbracket,prsrhlpr_t::e_hold)) + { + break; + } + } + + if (!token_is(token_t::e_rcrlbracket)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR103 - Expected '}' at end of [*] statement", + exprtk_error_location)); + + return error_node(); + } + + const expression_node_ptr result = expression_generator_.multi_switch_statement(arg_list); + + svd.delete_ptr = (0 == result); + + return result; + } + + inline expression_node_ptr parse_vararg_function() + { + std::vector arg_list; + + details::operator_type opt_type = details::e_default; + const std::string symbol = current_token().value; + + if (details::imatch(symbol,"~")) + { + next_token(); + return check_block_statement_closure(parse_multi_sequence()); + } + else if (details::imatch(symbol,"[*]")) + { + return check_block_statement_closure(parse_multi_switch_statement()); + } + else if (details::imatch(symbol, "avg" )) opt_type = details::e_avg ; + else if (details::imatch(symbol, "mand")) opt_type = details::e_mand; + else if (details::imatch(symbol, "max" )) opt_type = details::e_max ; + else if (details::imatch(symbol, "min" )) opt_type = details::e_min ; + else if (details::imatch(symbol, "mor" )) opt_type = details::e_mor ; + else if (details::imatch(symbol, "mul" )) opt_type = details::e_prod; + else if (details::imatch(symbol, "sum" )) opt_type = details::e_sum ; + else + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR104 - Unsupported built-in vararg function: " + symbol, + exprtk_error_location)); + + return error_node(); + } + + scoped_vec_delete svd((*this), arg_list); + + lodge_symbol(symbol, e_st_function); + + next_token(); + + if (!token_is(token_t::e_lbracket)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR105 - Expected '(' for call to vararg function: " + symbol, + exprtk_error_location)); + + return error_node(); + } + + if (token_is(token_t::e_rbracket)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR106 - vararg function: " + symbol + + " requires at least one input parameter", + exprtk_error_location)); + + return error_node(); + } + + for ( ; ; ) + { + expression_node_ptr arg = parse_expression(); + + if (0 == arg) + return error_node(); + else + arg_list.push_back(arg); + + if (token_is(token_t::e_rbracket)) + break; + else if (!token_is(token_t::e_comma)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR107 - Expected ',' for call to vararg function: " + symbol, + exprtk_error_location)); + + return error_node(); + } + } + + const expression_node_ptr result = expression_generator_.vararg_function(opt_type,arg_list); + + svd.delete_ptr = (0 == result); + return result; + } + + #ifndef exprtk_disable_string_capabilities + inline expression_node_ptr parse_string_range_statement(expression_node_ptr& expression) + { + if (!token_is(token_t::e_lsqrbracket)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR108 - Expected '[' as start of string range definition", + exprtk_error_location)); + + free_node(node_allocator_, expression); + + return error_node(); + } + else if (token_is(token_t::e_rsqrbracket)) + { + return node_allocator_.allocate >(expression); + } + + range_t rp; + + if (!parse_range(rp,true)) + { + free_node(node_allocator_, expression); + + return error_node(); + } + + expression_node_ptr result = expression_generator_(expression,rp); + + if (0 == result) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR109 - Failed to generate string range node", + exprtk_error_location)); + + free_node(node_allocator_, expression); + rp.free(); + } + + rp.clear(); + + if (result && result->valid()) + { + return result; + } + + set_error(make_error( + parser_error::e_synthesis, + current_token(), + "ERR110 - Failed to synthesize node: string_range_node", + exprtk_error_location)); + + free_node(node_allocator_, result); + rp.free(); + return error_node(); + } + #else + inline expression_node_ptr parse_string_range_statement(expression_node_ptr&) + { + return error_node(); + } + #endif + + inline bool parse_pending_string_rangesize(expression_node_ptr& expression) + { + // Allow no more than 100 range calls, eg: s[][][]...[][] + const std::size_t max_rangesize_parses = 100; + + std::size_t i = 0; + + while + ( + (0 != expression) && + (i++ < max_rangesize_parses) && + error_list_.empty() && + is_generally_string_node(expression) && + token_is(token_t::e_lsqrbracket,prsrhlpr_t::e_hold) + ) + { + expression = parse_string_range_statement(expression); + } + + return (i > 1); + } + + inline void parse_pending_vector_index_operator(expression_node_ptr& expression) + { + if + ( + (0 != expression) && + error_list_.empty() && + is_ivector_node(expression) + ) + { + if ( + settings_.commutative_check_enabled() && + token_is(token_t::e_mul,prsrhlpr_t::e_hold) && + peek_token_is(token_t::e_lsqrbracket) + ) + { + token_is(token_t::e_mul); + token_is(token_t::e_lsqrbracket); + } + else if (token_is(token_t::e_lsqrbracket,prsrhlpr_t::e_hold)) + { + token_is(token_t::e_lsqrbracket); + } + else if ( + token_is(token_t::e_rbracket,prsrhlpr_t::e_hold) && + peek_token_is(token_t::e_lsqrbracket) + ) + { + token_is(token_t::e_rbracket ); + token_is(token_t::e_lsqrbracket); + } + else + return; + + details::vector_interface* vi = dynamic_cast*>(expression); + + if (vi) + { + details::vector_holder& vec = vi->vec()->vec_holder(); + const std::string vector_name = sem_.get_vector_name(vec.data()); + expression_node_ptr index = parse_vector_index(vector_name); + + if (index) + { + expression = synthesize_vector_element(vector_name, &vec, expression, index); + return; + } + } + + free_node(node_allocator_, expression); + expression = error_node(); + } + } + + template class Sequence> + inline expression_node_ptr simplify(Sequence& expression_list, + Sequence& side_effect_list, + const bool specialise_on_final_type = false) + { + if (expression_list.empty()) + return error_node(); + else if (1 == expression_list.size()) + return expression_list[0]; + + Sequence tmp_expression_list; + + exprtk_debug(("simplify() - expression_list.size: %d side_effect_list.size(): %d\n", + static_cast(expression_list .size()), + static_cast(side_effect_list.size()))); + + bool return_node_present = false; + + for (std::size_t i = 0; i < (expression_list.size() - 1); ++i) + { + if (is_variable_node(expression_list[i])) + continue; + else if ( + is_return_node (expression_list[i]) || + is_break_node (expression_list[i]) || + is_continue_node(expression_list[i]) + ) + { + tmp_expression_list.push_back(expression_list[i]); + + // Remove all subexpressions after first short-circuit + // node has been encountered. + + for (std::size_t j = i + 1; j < expression_list.size(); ++j) + { + free_node(node_allocator_, expression_list[j]); + } + + return_node_present = true; + + break; + } + else if ( + is_constant_node(expression_list[i]) || + is_null_node (expression_list[i]) || + !side_effect_list[i] + ) + { + free_node(node_allocator_, expression_list[i]); + continue; + } + else + tmp_expression_list.push_back(expression_list[i]); + } + + if (!return_node_present) + { + tmp_expression_list.push_back(expression_list.back()); + } + + expression_list.swap(tmp_expression_list); + + if (tmp_expression_list.size() > expression_list.size()) + { + exprtk_debug(("simplify() - Reduced subexpressions from %d to %d\n", + static_cast(tmp_expression_list.size()), + static_cast(expression_list .size()))); + } + + if ( + return_node_present || + side_effect_list.back() || + (expression_list.size() > 1) + ) + state_.activate_side_effect("simplify()"); + + if (1 == expression_list.size()) + return expression_list[0]; + else if (specialise_on_final_type && is_generally_string_node(expression_list.back())) + return expression_generator_.vararg_function(details::e_smulti,expression_list); + else + return expression_generator_.vararg_function(details::e_multi,expression_list); + } + + inline expression_node_ptr parse_multi_sequence(const std::string& source = "", + const bool enforce_crlbrackets = false) + { + token_t::token_type open_bracket = token_t::e_lcrlbracket; + token_t::token_type close_bracket = token_t::e_rcrlbracket; + token_t::token_type separator = token_t::e_eof; + + if (!token_is(open_bracket)) + { + if (!enforce_crlbrackets && token_is(token_t::e_lbracket)) + { + open_bracket = token_t::e_lbracket; + close_bracket = token_t::e_rbracket; + separator = token_t::e_comma; + } + else + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR111 - Expected '" + token_t::to_str(open_bracket) + "' for call to multi-sequence" + + ((!source.empty()) ? std::string(" section of " + source): ""), + exprtk_error_location)); + + return error_node(); + } + } + else if (token_is(close_bracket)) + { + return node_allocator_.allocate >(); + } + + std::vector arg_list; + std::vector side_effect_list; + + scoped_vec_delete svd((*this), arg_list); + + scope_handler sh(*this); + + scoped_bool_or_restorer sbr(state_.side_effect_present); + + for ( ; ; ) + { + state_.side_effect_present = false; + + expression_node_ptr arg = parse_expression(); + + if (0 == arg) + return error_node(); + else + { + arg_list.push_back(arg); + side_effect_list.push_back(state_.side_effect_present); + } + + if (token_is(close_bracket)) + break; + + const bool is_next_close = peek_token_is(close_bracket); + + if (!token_is(separator) && is_next_close) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR112 - Expected '" + lexer::token::seperator_to_str(separator) + "' for call to multi-sequence section of " + source, + exprtk_error_location)); + + return error_node(); + } + + if (token_is(close_bracket)) + break; + } + + expression_node_ptr result = simplify(arg_list, side_effect_list, source.empty()); + + svd.delete_ptr = (0 == result); + return result; + } + + inline bool parse_range(range_t& rp, const bool skip_lsqr = false) + { + // Examples of valid ranges: + // 1. [1:5] -> [1,5) + // 2. [ :5] -> [0,5) + // 3. [1: ] -> [1,end) + // 4. [x:y] -> [x,y) where x <= y + // 5. [x+1:y/2] -> [x+1,y/2) where x+1 <= y/2 + // 6. [ :y] -> [0,y) where 0 <= y + // 7. [x: ] -> [x,end) where x <= end + + rp.clear(); + + if (!skip_lsqr && !token_is(token_t::e_lsqrbracket)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR113 - Expected '[' for start of range", + exprtk_error_location)); + + return false; + } + + if (token_is(token_t::e_colon)) + { + rp.n0_c.first = true; + rp.n0_c.second = 0; + rp.cache.first = 0; + } + else + { + expression_node_ptr r0 = parse_expression(); + + if (0 == r0) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR114 - Failed parse begin section of range", + exprtk_error_location)); + + return false; + } + else if (is_constant_node(r0)) + { + const T r0_value = r0->value(); + + if (r0_value >= T(0)) + { + rp.n0_c.first = true; + rp.n0_c.second = static_cast(details::numeric::to_int64(r0_value)); + rp.cache.first = rp.n0_c.second; + } + + free_node(node_allocator_, r0); + + if (r0_value < T(0)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR115 - Range lower bound less than zero! Constraint: r0 >= 0", + exprtk_error_location)); + + return false; + } + } + else + { + rp.n0_e.first = true; + rp.n0_e.second = r0; + } + + if (!token_is(token_t::e_colon)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR116 - Expected ':' for break in range", + exprtk_error_location)); + + rp.free(); + + return false; + } + } + + if (token_is(token_t::e_rsqrbracket)) + { + rp.n1_c.first = true; + rp.n1_c.second = std::numeric_limits::max(); + } + else + { + expression_node_ptr r1 = parse_expression(); + + if (0 == r1) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR117 - Failed parse end section of range", + exprtk_error_location)); + + rp.free(); + + return false; + } + else if (is_constant_node(r1)) + { + const T r1_value = r1->value(); + + if (r1_value >= T(0)) + { + rp.n1_c.first = true; + rp.n1_c.second = static_cast(details::numeric::to_int64(r1_value)); + rp.cache.second = rp.n1_c.second; + } + + free_node(node_allocator_, r1); + + if (r1_value < T(0)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR118 - Range upper bound less than zero! Constraint: r1 >= 0", + exprtk_error_location)); + + rp.free(); + + return false; + } + } + else + { + rp.n1_e.first = true; + rp.n1_e.second = r1; + } + + if (!token_is(token_t::e_rsqrbracket)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR119 - Expected ']' for start of range", + exprtk_error_location)); + + rp.free(); + + return false; + } + } + + if (rp.const_range()) + { + std::size_t r0 = 0; + std::size_t r1 = 0; + + bool rp_result = false; + + try + { + rp_result = rp(r0, r1); + } + catch (std::runtime_error&) + {} + + if (!rp_result || (r0 > r1)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR120 - Invalid range, Constraint: r0 <= r1", + exprtk_error_location)); + + return false; + } + } + + return true; + } + + inline void lodge_symbol(const std::string& symbol, + const symbol_type st) + { + dec_.add_symbol(symbol,st); + } + + #ifndef exprtk_disable_string_capabilities + inline expression_node_ptr parse_string() + { + const std::string symbol = current_token().value; + + typedef details::stringvar_node* strvar_node_t; + + expression_node_ptr result = error_node(); + strvar_node_t const_str_node = static_cast(0); + + scope_element& se = sem_.get_active_element(symbol); + + if (scope_element::e_string == se.type) + { + se.active = true; + result = se.str_node; + lodge_symbol(symbol, e_st_local_string); + } + else + { + typedef typename symtab_store::string_context str_ctxt_t; + str_ctxt_t str_ctx = symtab_store_.get_string_context(symbol); + + if ((0 == str_ctx.str_var) || !symtab_store_.is_conststr_stringvar(symbol)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR121 - Unknown string symbol", + exprtk_error_location)); + + return error_node(); + } + + assert(str_ctx.str_var != 0); + assert(str_ctx.symbol_table != 0); + + result = str_ctx.str_var; + + if (symtab_store_.is_constant_string(symbol)) + { + const_str_node = static_cast(result); + result = expression_generator_(const_str_node->str()); + } + else if (symbol_table_t::e_immutable == str_ctx.symbol_table->mutability()) + { + lodge_immutable_symbol( + current_token(), + make_memory_range(str_ctx.str_var->base(), str_ctx.str_var->size())); + } + + lodge_symbol(symbol, e_st_string); + } + + if (peek_token_is(token_t::e_lsqrbracket)) + { + next_token(); + + if (peek_token_is(token_t::e_rsqrbracket)) + { + next_token(); + next_token(); + + if (const_str_node) + { + free_node(node_allocator_, result); + + return expression_generator_(T(const_str_node->size())); + } + else + return node_allocator_.allocate > + (static_cast*>(result)->ref()); + } + + range_t rp; + + if (!parse_range(rp)) + { + free_node(node_allocator_, result); + + return error_node(); + } + else if (const_str_node) + { + free_node(node_allocator_, result); + result = expression_generator_(const_str_node->ref(),rp); + } + else + result = expression_generator_(static_cast*> + (result)->ref(), rp); + + if (result) + rp.clear(); + } + else + next_token(); + + return result; + } + #else + inline expression_node_ptr parse_string() + { + return error_node(); + } + #endif + + #ifndef exprtk_disable_string_capabilities + inline expression_node_ptr parse_const_string() + { + const std::string const_str = current_token().value; + expression_node_ptr result = expression_generator_(const_str); + + if (peek_token_is(token_t::e_lsqrbracket)) + { + next_token(); + + if (peek_token_is(token_t::e_rsqrbracket)) + { + next_token(); + next_token(); + + free_node(node_allocator_, result); + + return expression_generator_(T(const_str.size())); + } + + range_t rp; + + if (!parse_range(rp)) + { + free_node(node_allocator_, result); + rp.free(); + + return error_node(); + } + + free_node(node_allocator_, result); + + if (rp.n1_c.first && (rp.n1_c.second == std::numeric_limits::max())) + { + rp.n1_c.second = const_str.size() - 1; + rp.cache.second = rp.n1_c.second; + } + + if ( + (rp.n0_c.first && (rp.n0_c.second >= const_str.size())) || + (rp.n1_c.first && (rp.n1_c.second >= const_str.size())) + ) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR122 - Overflow in range for string: '" + const_str + "'[" + + (rp.n0_c.first ? details::to_str(static_cast(rp.n0_c.second)) : "?") + ":" + + (rp.n1_c.first ? details::to_str(static_cast(rp.n1_c.second)) : "?") + "]", + exprtk_error_location)); + + rp.free(); + + return error_node(); + } + + result = expression_generator_(const_str,rp); + + if (result) + rp.clear(); + } + else + next_token(); + + return result; + } + #else + inline expression_node_ptr parse_const_string() + { + return error_node(); + } + #endif + + inline expression_node_ptr parse_vector_index(const std::string& vector_name = "") + { + expression_node_ptr index_expr = error_node(); + + if (0 == (index_expr = parse_expression())) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR123 - Failed to parse index for vector: '" + vector_name + "'", + exprtk_error_location)); + + return error_node(); + } + else if (!token_is(token_t::e_rsqrbracket)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR124 - Expected ']' for index of vector: '" + vector_name + "'", + exprtk_error_location)); + + free_node(node_allocator_, index_expr); + + return error_node(); + } + + return index_expr; + } + + inline expression_node_ptr parse_vector() + { + const std::string vector_name = current_token().value; + + vector_holder_ptr vec = vector_holder_ptr(0); + + const scope_element& se = sem_.get_active_element(vector_name); + + if ( + !details::imatch(se.name, vector_name) || + (se.depth > state_.scope_depth) || + (scope_element::e_vector != se.type) + ) + { + typedef typename symtab_store::vector_context vec_ctxt_t; + vec_ctxt_t vec_ctx = symtab_store_.get_vector_context(vector_name); + + if (0 == vec_ctx.vector_holder) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR125 - Symbol '" + vector_name + " not a vector", + exprtk_error_location)); + + return error_node(); + } + + assert(0 != vec_ctx.vector_holder); + assert(0 != vec_ctx.symbol_table ); + + vec = vec_ctx.vector_holder; + + if (symbol_table_t::e_immutable == vec_ctx.symbol_table->mutability()) + { + lodge_immutable_symbol( + current_token(), + make_memory_range(vec->data(), vec->size())); + } + } + else + { + vec = se.vec_node; + } + + assert(0 != vec); + + next_token(); + + if (!token_is(token_t::e_lsqrbracket)) + { + return node_allocator_.allocate(vec); + } + else if (token_is(token_t::e_rsqrbracket)) + { + return (vec->rebaseable()) ? + node_allocator_.allocate(vec) : + expression_generator_(T(vec->size())); + } + + expression_node_ptr index_expr = parse_vector_index(vector_name); + + if (index_expr) + { + expression_node_ptr vec_node = node_allocator_.allocate(vec); + + return synthesize_vector_element(vector_name, vec, vec_node, index_expr); + } + + return error_node(); + } + + inline expression_node_ptr synthesize_vector_element(const std::string& vector_name, + vector_holder_ptr vec, + expression_node_ptr vec_node, + expression_node_ptr index_expr) + { + // Perform compile-time range check + if (details::is_constant_node(index_expr)) + { + const std::size_t index = static_cast(details::numeric::to_int32(index_expr->value())); + const std::size_t vec_size = vec->size(); + + if (index >= vec_size) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR126 - Index of " + details::to_str(index) + " out of range for " + "vector '" + vector_name + "' of size " + details::to_str(vec_size), + exprtk_error_location)); + + free_node(node_allocator_, vec_node ); + free_node(node_allocator_, index_expr); + + return error_node(); + } + } + + return expression_generator_.vector_element(vector_name, vec, vec_node, index_expr); + } + + inline expression_node_ptr parse_vararg_function_call(ivararg_function* vararg_function, const std::string& vararg_function_name) + { + std::vector arg_list; + + scoped_vec_delete svd((*this), arg_list); + + next_token(); + + if (token_is(token_t::e_lbracket)) + { + if (token_is(token_t::e_rbracket)) + { + if (!vararg_function->allow_zero_parameters()) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR127 - Zero parameter call to vararg function: " + + vararg_function_name + " not allowed", + exprtk_error_location)); + + return error_node(); + } + } + else + { + for ( ; ; ) + { + expression_node_ptr arg = parse_expression(); + + if (0 == arg) + return error_node(); + else + arg_list.push_back(arg); + + if (token_is(token_t::e_rbracket)) + break; + else if (!token_is(token_t::e_comma)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR128 - Expected ',' for call to vararg function: " + + vararg_function_name, + exprtk_error_location)); + + return error_node(); + } + } + } + } + else if (!vararg_function->allow_zero_parameters()) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR129 - Zero parameter call to vararg function: " + + vararg_function_name + " not allowed", + exprtk_error_location)); + + return error_node(); + } + + if (arg_list.size() < vararg_function->min_num_args()) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR130 - Invalid number of parameters to call to vararg function: " + + vararg_function_name + ", require at least " + + details::to_str(static_cast(vararg_function->min_num_args())) + " parameters", + exprtk_error_location)); + + return error_node(); + } + else if (arg_list.size() > vararg_function->max_num_args()) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR131 - Invalid number of parameters to call to vararg function: " + + vararg_function_name + ", require no more than " + + details::to_str(static_cast(vararg_function->max_num_args())) + " parameters", + exprtk_error_location)); + + return error_node(); + } + + expression_node_ptr result = expression_generator_.vararg_function_call(vararg_function,arg_list); + + svd.delete_ptr = (0 == result); + + return result; + } + + class type_checker + { + public: + + enum return_type_t + { + e_overload = ' ', + e_numeric = 'T', + e_string = 'S' + }; + + struct function_prototype_t + { + return_type_t return_type; + std::string param_seq; + }; + + typedef parser parser_t; + typedef std::vector function_definition_list_t; + + type_checker(parser_t& p, + const std::string& func_name, + const std::string& func_prototypes, + const return_type_t default_return_type) + : invalid_state_(true) + , parser_(p) + , function_name_(func_name) + , default_return_type_(default_return_type) + { + parse_function_prototypes(func_prototypes); + } + + bool verify(const std::string& param_seq, std::size_t& pseq_index) + { + if (function_definition_list_.empty()) + return true; + + std::vector > error_list; + + for (std::size_t i = 0; i < function_definition_list_.size(); ++i) + { + details::char_t diff_value = 0; + std::size_t diff_index = 0; + + const bool result = details::sequence_match(function_definition_list_[i].param_seq, + param_seq, + diff_index, diff_value); + + if (result) + { + pseq_index = i; + return true; + } + else + error_list.push_back(std::make_pair(diff_index, diff_value)); + } + + if (1 == error_list.size()) + { + parser_.set_error(make_error( + parser_error::e_syntax, + parser_.current_token(), + "ERR132 - Failed parameter type check for function '" + function_name_ + "', " + "Expected '" + function_definition_list_[0].param_seq + + "' call set: '" + param_seq + "'", + exprtk_error_location)); + } + else + { + // find first with largest diff_index; + std::size_t max_diff_index = 0; + + for (std::size_t i = 1; i < error_list.size(); ++i) + { + if (error_list[i].first > error_list[max_diff_index].first) + { + max_diff_index = i; + } + } + + parser_.set_error(make_error( + parser_error::e_syntax, + parser_.current_token(), + "ERR133 - Failed parameter type check for function '" + function_name_ + "', " + "Best match: '" + function_definition_list_[max_diff_index].param_seq + + "' call set: '" + param_seq + "'", + exprtk_error_location)); + } + + return false; + } + + std::size_t paramseq_count() const + { + return function_definition_list_.size(); + } + + std::string paramseq(const std::size_t& index) const + { + return function_definition_list_[index].param_seq; + } + + return_type_t return_type(const std::size_t& index) const + { + return function_definition_list_[index].return_type; + } + + bool invalid() const + { + return !invalid_state_; + } + + bool allow_zero_parameters() const + { + + for (std::size_t i = 0; i < function_definition_list_.size(); ++i) + { + if (std::string::npos != function_definition_list_[i].param_seq.find("Z")) + { + return true; + } + } + + return false; + } + + private: + + std::vector split_param_seq(const std::string& param_seq, const details::char_t delimiter = '|') const + { + std::string::const_iterator current_begin = param_seq.begin(); + std::string::const_iterator iter = param_seq.begin(); + + std::vector result; + + while (iter != param_seq.end()) + { + if (*iter == delimiter) + { + result.push_back(std::string(current_begin, iter)); + current_begin = ++iter; + } + else + ++iter; + } + + if (current_begin != iter) + { + result.push_back(std::string(current_begin, iter)); + } + + return result; + } + + inline bool is_valid_token(std::string param_seq, + function_prototype_t& funcproto) const + { + // Determine return type + funcproto.return_type = default_return_type_; + + if (param_seq.size() > 2) + { + if (':' == param_seq[1]) + { + // Note: Only overloaded igeneric functions can have return + // type definitions. + if (type_checker::e_overload != default_return_type_) + return false; + + switch (param_seq[0]) + { + case 'T' : funcproto.return_type = type_checker::e_numeric; + break; + + case 'S' : funcproto.return_type = type_checker::e_string; + break; + + default : return false; + } + + param_seq.erase(0,2); + } + } + + if ( + (std::string::npos != param_seq.find("?*")) || + (std::string::npos != param_seq.find("**")) + ) + { + return false; + } + else if ( + (std::string::npos == param_seq.find_first_not_of("STV*?|")) || + ("Z" == param_seq) + ) + { + funcproto.param_seq = param_seq; + return true; + } + + return false; + } + + void parse_function_prototypes(const std::string& func_prototypes) + { + if (func_prototypes.empty()) + return; + + std::vector param_seq_list = split_param_seq(func_prototypes); + + typedef std::map param_seq_map_t; + param_seq_map_t param_seq_map; + + for (std::size_t i = 0; i < param_seq_list.size(); ++i) + { + function_prototype_t func_proto; + + if (!is_valid_token(param_seq_list[i], func_proto)) + { + invalid_state_ = false; + + parser_.set_error(make_error( + parser_error::e_syntax, + parser_.current_token(), + "ERR134 - Invalid parameter sequence of '" + param_seq_list[i] + + "' for function: " + function_name_, + exprtk_error_location)); + return; + } + + param_seq_map_t::const_iterator seq_itr = param_seq_map.find(param_seq_list[i]); + + if (param_seq_map.end() != seq_itr) + { + invalid_state_ = false; + + parser_.set_error(make_error( + parser_error::e_syntax, + parser_.current_token(), + "ERR135 - Function '" + function_name_ + "' has a parameter sequence conflict between " + + "pseq_idx[" + details::to_str(seq_itr->second) + "] and" + + "pseq_idx[" + details::to_str(i) + "] " + + "param seq: " + param_seq_list[i], + exprtk_error_location)); + return; + } + + function_definition_list_.push_back(func_proto); + } + } + + type_checker(const type_checker&) exprtk_delete; + type_checker& operator=(const type_checker&) exprtk_delete; + + bool invalid_state_; + parser_t& parser_; + std::string function_name_; + const return_type_t default_return_type_; + function_definition_list_t function_definition_list_; + }; + + inline expression_node_ptr parse_generic_function_call(igeneric_function* function, const std::string& function_name) + { + std::vector arg_list; + + scoped_vec_delete svd((*this), arg_list); + + next_token(); + + std::string param_type_list; + + type_checker tc( + (*this), + function_name, + function->parameter_sequence, + type_checker::e_string); + + if (tc.invalid()) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR136 - Type checker instantiation failure for generic function: " + function_name, + exprtk_error_location)); + + return error_node(); + } + + if (token_is(token_t::e_lbracket)) + { + if (token_is(token_t::e_rbracket)) + { + if ( + !function->allow_zero_parameters() && + !tc .allow_zero_parameters() + ) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR137 - Zero parameter call to generic function: " + + function_name + " not allowed", + exprtk_error_location)); + + return error_node(); + } + } + else + { + for ( ; ; ) + { + expression_node_ptr arg = parse_expression(); + + if (0 == arg) + return error_node(); + + if (is_ivector_node(arg)) + param_type_list += 'V'; + else if (is_generally_string_node(arg)) + param_type_list += 'S'; + else // Everything else is assumed to be a scalar returning expression + param_type_list += 'T'; + + arg_list.push_back(arg); + + if (token_is(token_t::e_rbracket)) + break; + else if (!token_is(token_t::e_comma)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR138 - Expected ',' for call to generic function: " + function_name, + exprtk_error_location)); + + return error_node(); + } + } + } + } + else if ( + !function->parameter_sequence.empty() && + function->allow_zero_parameters () && + !tc .allow_zero_parameters () + ) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR139 - Zero parameter call to generic function: " + + function_name + " not allowed", + exprtk_error_location)); + + return error_node(); + } + + std::size_t param_seq_index = 0; + + if ( + state_.type_check_enabled && + !tc.verify(param_type_list, param_seq_index) + ) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR140 - Invalid input parameter sequence for call to generic function: " + function_name, + exprtk_error_location)); + + return error_node(); + } + + expression_node_ptr result = + (tc.paramseq_count() <= 1) ? + expression_generator_ + .generic_function_call(function, arg_list) : + expression_generator_ + .generic_function_call(function, arg_list, param_seq_index); + + svd.delete_ptr = (0 == result); + + return result; + } + + inline bool parse_igeneric_function_params(std::string& param_type_list, + std::vector& arg_list, + const std::string& function_name, + igeneric_function* function, + const type_checker& tc) + { + if (token_is(token_t::e_lbracket)) + { + if (token_is(token_t::e_rbracket)) + { + if ( + !function->allow_zero_parameters() && + !tc .allow_zero_parameters() + ) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR141 - Zero parameter call to generic function: " + + function_name + " not allowed", + exprtk_error_location)); + + return false; + } + } + else + { + for ( ; ; ) + { + expression_node_ptr arg = parse_expression(); + + if (0 == arg) + return false; + + if (is_ivector_node(arg)) + param_type_list += 'V'; + else if (is_generally_string_node(arg)) + param_type_list += 'S'; + else // Everything else is a scalar returning expression + param_type_list += 'T'; + + arg_list.push_back(arg); + + if (token_is(token_t::e_rbracket)) + break; + else if (!token_is(token_t::e_comma)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR142 - Expected ',' for call to string function: " + function_name, + exprtk_error_location)); + + return false; + } + } + } + + return true; + } + else + return false; + } + + #ifndef exprtk_disable_string_capabilities + inline expression_node_ptr parse_string_function_call(igeneric_function* function, const std::string& function_name) + { + // Move pass the function name + next_token(); + + std::string param_type_list; + + type_checker tc((*this), function_name, function->parameter_sequence, type_checker::e_string); + + if ( + (!function->parameter_sequence.empty()) && + (0 == tc.paramseq_count()) + ) + { + return error_node(); + } + + std::vector arg_list; + scoped_vec_delete svd((*this), arg_list); + + if (!parse_igeneric_function_params(param_type_list, arg_list, function_name, function, tc)) + { + return error_node(); + } + + std::size_t param_seq_index = 0; + + if (!tc.verify(param_type_list, param_seq_index)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR143 - Invalid input parameter sequence for call to string function: " + function_name, + exprtk_error_location)); + + return error_node(); + } + + expression_node_ptr result = + (tc.paramseq_count() <= 1) ? + expression_generator_ + .string_function_call(function, arg_list) : + expression_generator_ + .string_function_call(function, arg_list, param_seq_index); + + svd.delete_ptr = (0 == result); + + return result; + } + + inline expression_node_ptr parse_overload_function_call(igeneric_function* function, const std::string& function_name) + { + // Move pass the function name + next_token(); + + std::string param_type_list; + + type_checker tc((*this), function_name, function->parameter_sequence, type_checker::e_overload); + + if ( + (!function->parameter_sequence.empty()) && + (0 == tc.paramseq_count()) + ) + { + return error_node(); + } + + std::vector arg_list; + scoped_vec_delete svd((*this), arg_list); + + if (!parse_igeneric_function_params(param_type_list, arg_list, function_name, function, tc)) + { + return error_node(); + } + + std::size_t param_seq_index = 0; + + if (!tc.verify(param_type_list, param_seq_index)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR144 - Invalid input parameter sequence for call to overloaded function: " + function_name, + exprtk_error_location)); + + return error_node(); + } + + expression_node_ptr result = error_node(); + + if (type_checker::e_numeric == tc.return_type(param_seq_index)) + { + if (tc.paramseq_count() <= 1) + result = expression_generator_ + .generic_function_call(function, arg_list); + else + result = expression_generator_ + .generic_function_call(function, arg_list, param_seq_index); + } + else if (type_checker::e_string == tc.return_type(param_seq_index)) + { + if (tc.paramseq_count() <= 1) + result = expression_generator_ + .string_function_call(function, arg_list); + else + result = expression_generator_ + .string_function_call(function, arg_list, param_seq_index); + } + else + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR145 - Invalid return type for call to overloaded function: " + function_name, + exprtk_error_location)); + } + + svd.delete_ptr = (0 == result); + return result; + } + #endif + + template + struct parse_special_function_impl + { + static inline expression_node_ptr process(parser& p, const details::operator_type opt_type, const std::string& sf_name) + { + expression_node_ptr branch[NumberOfParameters]; + expression_node_ptr result = error_node(); + + std::fill_n(branch, NumberOfParameters, reinterpret_cast(0)); + + scoped_delete sd(p,branch); + + p.next_token(); + + if (!p.token_is(token_t::e_lbracket)) + { + p.set_error(make_error( + parser_error::e_syntax, + p.current_token(), + "ERR146 - Expected '(' for special function '" + sf_name + "'", + exprtk_error_location)); + + return error_node(); + } + + for (std::size_t i = 0; i < NumberOfParameters; ++i) + { + branch[i] = p.parse_expression(); + + if (0 == branch[i]) + { + return p.error_node(); + } + else if (i < (NumberOfParameters - 1)) + { + if (!p.token_is(token_t::e_comma)) + { + p.set_error(make_error( + parser_error::e_syntax, + p.current_token(), + "ERR147 - Expected ',' before next parameter of special function '" + sf_name + "'", + exprtk_error_location)); + + return p.error_node(); + } + } + } + + if (!p.token_is(token_t::e_rbracket)) + { + p.set_error(make_error( + parser_error::e_syntax, + p.current_token(), + "ERR148 - Invalid number of parameters for special function '" + sf_name + "'", + exprtk_error_location)); + + return p.error_node(); + } + else + result = p.expression_generator_.special_function(opt_type,branch); + + sd.delete_ptr = (0 == result); + + return result; + } + }; + + inline expression_node_ptr parse_special_function() + { + const std::string sf_name = current_token().value; + + // Expect: $fDD(expr0,expr1,expr2) or $fDD(expr0,expr1,expr2,expr3) + if ( + !details::is_digit(sf_name[2]) || + !details::is_digit(sf_name[3]) + ) + { + set_error(make_error( + parser_error::e_token, + current_token(), + "ERR149 - Invalid special function[1]: " + sf_name, + exprtk_error_location)); + + return error_node(); + } + + const int id = (sf_name[2] - '0') * 10 + + (sf_name[3] - '0'); + + if (id >= details::e_sffinal) + { + set_error(make_error( + parser_error::e_token, + current_token(), + "ERR150 - Invalid special function[2]: " + sf_name, + exprtk_error_location)); + + return error_node(); + } + + const int sf_3_to_4 = details::e_sf48; + const details::operator_type opt_type = details::operator_type(id + 1000); + const std::size_t NumberOfParameters = (id < (sf_3_to_4 - 1000)) ? 3U : 4U; + + switch (NumberOfParameters) + { + case 3 : return parse_special_function_impl::process((*this), opt_type, sf_name); + case 4 : return parse_special_function_impl::process((*this), opt_type, sf_name); + default : return error_node(); + } + } + + inline expression_node_ptr parse_null_statement() + { + next_token(); + return node_allocator_.allocate >(); + } + + #ifndef exprtk_disable_break_continue + inline expression_node_ptr parse_break_statement() + { + if (state_.parsing_break_stmt) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR151 - Invoking 'break' within a break call is not allowed", + exprtk_error_location)); + + return error_node(); + } + else if (0 == state_.parsing_loop_stmt_count) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR152 - Invalid use of 'break', allowed only in the scope of a loop", + exprtk_error_location)); + + return error_node(); + } + + scoped_bool_negator sbn(state_.parsing_break_stmt); + + if (!brkcnt_list_.empty()) + { + next_token(); + + brkcnt_list_.front() = true; + + expression_node_ptr return_expr = error_node(); + + if (token_is(token_t::e_lsqrbracket)) + { + if (0 == (return_expr = parse_expression())) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR153 - Failed to parse return expression for 'break' statement", + exprtk_error_location)); + + return error_node(); + } + else if (!token_is(token_t::e_rsqrbracket)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR154 - Expected ']' at the completion of break's return expression", + exprtk_error_location)); + + free_node(node_allocator_, return_expr); + + return error_node(); + } + } + + state_.activate_side_effect("parse_break_statement()"); + + return node_allocator_.allocate >(return_expr); + } + else + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR155 - Invalid use of 'break', allowed only in the scope of a loop", + exprtk_error_location)); + } + + return error_node(); + } + + inline expression_node_ptr parse_continue_statement() + { + if (0 == state_.parsing_loop_stmt_count) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR156 - Invalid use of 'continue', allowed only in the scope of a loop", + exprtk_error_location)); + + return error_node(); + } + else + { + next_token(); + + brkcnt_list_.front() = true; + state_.activate_side_effect("parse_continue_statement()"); + + return node_allocator_.allocate >(); + } + } + #endif + + inline expression_node_ptr parse_define_vector_statement(const std::string& vec_name) + { + expression_node_ptr size_expression_node = error_node(); + + if (!token_is(token_t::e_lsqrbracket)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR157 - Expected '[' as part of vector size definition", + exprtk_error_location)); + + return error_node(); + } + else if (0 == (size_expression_node = parse_expression())) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR158 - Failed to determine size of vector '" + vec_name + "'", + exprtk_error_location)); + + return error_node(); + } + else if (!is_constant_node(size_expression_node)) + { + const bool is_rebaseble_vector = + (size_expression_node->type() == details::expression_node::e_vecsize) && + static_cast*>(size_expression_node)->vec_holder()->rebaseable(); + + free_node(node_allocator_, size_expression_node); + + const std::string error_msg = (is_rebaseble_vector) ? + std::string("Rebasable/Resizable vector cannot be used to define the size of vector") : + std::string("Expected a constant literal number as size of vector"); + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR159 - " + error_msg + " '" + vec_name + "'", + exprtk_error_location)); + + return error_node(); + } + + const T vector_size = size_expression_node->value(); + + free_node(node_allocator_, size_expression_node); + + const std::size_t max_vector_size = settings_.max_local_vector_size(); + + if ( + (vector_size <= T(0)) || + std::not_equal_to() + (T(0),vector_size - details::numeric::trunc(vector_size)) || + (static_cast(vector_size) > max_vector_size) + ) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR160 - Invalid vector size. Must be an integer in the " + "range [0," + details::to_str(static_cast(max_vector_size)) + "], size: " + + details::to_str(details::numeric::to_int32(vector_size)), + exprtk_error_location)); + + return error_node(); + } + + typename symbol_table_t::vector_holder_ptr vec_holder = typename symbol_table_t::vector_holder_ptr(0); + + const std::size_t vec_size = static_cast(details::numeric::to_int32(vector_size)); + const std::size_t predicted_total_lclsymb_size = sizeof(T) * vec_size + sem_.total_local_symb_size_bytes(); + + if (predicted_total_lclsymb_size > settings().max_total_local_symbol_size_bytes()) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR161 - Adding vector '" + vec_name + "' of size " + details::to_str(vec_size) + " bytes " + "will exceed max total local symbol size of: " + details::to_str(settings().max_total_local_symbol_size_bytes()) + " bytes, " + "current total size: " + details::to_str(sem_.total_local_symb_size_bytes()) + " bytes", + exprtk_error_location)); + + return error_node(); + } + + scope_element& se = sem_.get_element(vec_name); + + if (se.name == vec_name) + { + if (se.active) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR162 - Illegal redefinition of local vector: '" + vec_name + "'", + exprtk_error_location)); + + return error_node(); + } + else if ( + (se.size == vec_size) && + (scope_element::e_vector == se.type) + ) + { + vec_holder = se.vec_node; + se.active = true; + se.depth = state_.scope_depth; + se.ref_count++; + } + } + + if (0 == vec_holder) + { + scope_element nse; + nse.name = vec_name; + nse.active = true; + nse.ref_count = 1; + nse.type = scope_element::e_vector; + nse.depth = state_.scope_depth; + nse.size = vec_size; + nse.data = new T[vec_size]; + nse.vec_node = new typename scope_element::vector_holder_t(reinterpret_cast(nse.data),nse.size); + + details::set_zero_value(reinterpret_cast(nse.data),vec_size); + + if (!sem_.add_element(nse)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR163 - Failed to add new local vector '" + vec_name + "' to SEM", + exprtk_error_location)); + + sem_.free_element(nse); + + return error_node(); + } + + assert(sem_.total_local_symb_size_bytes() <= settings().max_total_local_symbol_size_bytes()); + + vec_holder = nse.vec_node; + + exprtk_debug(("parse_define_vector_statement() - INFO - Added new local vector: %s[%d]\n", + nse.name.c_str(), + static_cast(nse.size))); + } + + state_.activate_side_effect("parse_define_vector_statement()"); + + lodge_symbol(vec_name, e_st_local_vector); + + std::vector vec_initilizer_list; + + scoped_vec_delete svd((*this), vec_initilizer_list); + + bool single_value_initialiser = false; + bool range_value_initialiser = false; + bool vec_to_vec_initialiser = false; + bool null_initialisation = false; + + if (!token_is(token_t::e_rsqrbracket)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR164 - Expected ']' as part of vector size definition", + exprtk_error_location)); + + return error_node(); + } + else if (!token_is(token_t::e_eof, prsrhlpr_t::e_hold)) + { + if (!token_is(token_t::e_assign)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR165 - Expected ':=' as part of vector definition", + exprtk_error_location)); + + return error_node(); + } + else if (token_is(token_t::e_lsqrbracket)) + { + expression_node_ptr initialiser_component = parse_expression(); + + if (0 == initialiser_component) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR166 - Failed to parse first component of vector initialiser for vector: " + vec_name, + exprtk_error_location)); + + return error_node(); + } + + vec_initilizer_list.push_back(initialiser_component); + + if (token_is(token_t::e_colon)) + { + initialiser_component = parse_expression(); + + if (0 == initialiser_component) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR167 - Failed to parse second component of vector initialiser for vector: " + vec_name, + exprtk_error_location)); + + return error_node(); + } + + vec_initilizer_list.push_back(initialiser_component); + } + + if (!token_is(token_t::e_rsqrbracket)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR168 - Expected ']' to close single value vector initialiser", + exprtk_error_location)); + + return error_node(); + } + + switch (vec_initilizer_list.size()) + { + case 1 : single_value_initialiser = true; break; + case 2 : range_value_initialiser = true; break; + } + } + else if (!token_is(token_t::e_lcrlbracket)) + { + expression_node_ptr initialiser = error_node(); + + // Is this a vector to vector assignment and initialisation? + if (token_t::e_symbol == current_token().type) + { + // Is it a locally defined vector? + const scope_element& lcl_se = sem_.get_active_element(current_token().value); + + if (scope_element::e_vector == lcl_se.type) + { + if (0 != (initialiser = parse_expression())) + vec_initilizer_list.push_back(initialiser); + else + return error_node(); + } + // Are we dealing with a user defined vector? + else if (symtab_store_.is_vector(current_token().value)) + { + lodge_symbol(current_token().value, e_st_vector); + + if (0 != (initialiser = parse_expression())) + vec_initilizer_list.push_back(initialiser); + else + return error_node(); + } + // Are we dealing with a null initialisation vector definition? + else if (token_is(token_t::e_symbol,"null")) + null_initialisation = true; + } + + if (!null_initialisation) + { + if (0 == initialiser) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR169 - Expected '{' as part of vector initialiser list", + exprtk_error_location)); + + return error_node(); + } + else + vec_to_vec_initialiser = true; + } + } + else if (!token_is(token_t::e_rcrlbracket)) + { + for ( ; ; ) + { + expression_node_ptr initialiser = parse_expression(); + + if (0 == initialiser) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR170 - Expected '{' as part of vector initialiser list", + exprtk_error_location)); + + return error_node(); + } + else + vec_initilizer_list.push_back(initialiser); + + if (token_is(token_t::e_rcrlbracket)) + break; + + const bool is_next_close = peek_token_is(token_t::e_rcrlbracket); + + if (!token_is(token_t::e_comma) && is_next_close) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR171 - Expected ',' between vector initialisers", + exprtk_error_location)); + + return error_node(); + } + + if (token_is(token_t::e_rcrlbracket)) + break; + } + } + + if ( + !token_is(token_t::e_rbracket , prsrhlpr_t::e_hold) && + !token_is(token_t::e_rcrlbracket, prsrhlpr_t::e_hold) && + !token_is(token_t::e_rsqrbracket, prsrhlpr_t::e_hold) + ) + { + if (!token_is(token_t::e_eof,prsrhlpr_t::e_hold)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR172 - Expected ';' at end of vector definition", + exprtk_error_location)); + + return error_node(); + } + } + + if ( + !single_value_initialiser && + !range_value_initialiser && + (T(vec_initilizer_list.size()) > vector_size) + ) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR173 - Initialiser list larger than the number of elements in the vector: '" + vec_name + "'", + exprtk_error_location)); + + return error_node(); + } + } + + expression_node_ptr result = error_node(); + + if ( + (vec_initilizer_list.size() == 1) && + single_value_initialiser + ) + { + if (details::is_constant_node(vec_initilizer_list[0])) + { + // vector_init_zero_value_node var v[10] := [0] + if (T(0) == vec_initilizer_list[0]->value()) + { + result = node_allocator_ + .allocate >( + (*vec_holder)[0], + vec_size, + vec_initilizer_list); + } + else + { + // vector_init_single_constvalue_node var v[10] := [123] + result = node_allocator_ + .allocate >( + (*vec_holder)[0], + vec_size, + vec_initilizer_list); + } + } + else + { + // vector_init_single_value_node var v[10] := [123 + (x / y)] + result = node_allocator_ + .allocate >( + (*vec_holder)[0], + vec_size, + vec_initilizer_list); + } + } + else if ( + (vec_initilizer_list.size() == 2) && + range_value_initialiser + ) + { + bool base_const = details::is_constant_node(vec_initilizer_list[0]); + bool inc_const = details::is_constant_node(vec_initilizer_list[1]); + + if (base_const && inc_const) + { + // vector_init_single_value_node var v[10] := [1 : 3.5] + result = node_allocator_ + .allocate >( + (*vec_holder)[0], + vec_size, + vec_initilizer_list); + } + else if (base_const && !inc_const) + { + // vector_init_single_value_node var v[10] := [1 : x + y] + result = node_allocator_ + .allocate >( + (*vec_holder)[0], + vec_size, + vec_initilizer_list); + } + else if (!base_const && inc_const) + { + // vector_init_single_value_node var v[10] := [x + y : 3] + result = node_allocator_ + .allocate >( + (*vec_holder)[0], + vec_size, + vec_initilizer_list); + } + else if (!base_const && !inc_const) + { + // vector_init_single_value_node var v[10] := [x + y : z / w] + result = node_allocator_ + .allocate >( + (*vec_holder)[0], + vec_size, + vec_initilizer_list); + } + } + else if (null_initialisation) + result = expression_generator_(T(0.0)); + else if (vec_to_vec_initialiser) + { + expression_node_ptr vec_node = node_allocator_.allocate(vec_holder); + + result = expression_generator_( + details::e_assign, + vec_node, + vec_initilizer_list[0]); + } + else + { + result = node_allocator_ + .allocate >( + (*vec_holder)[0], + vec_size, + vec_initilizer_list, + single_value_initialiser); + } + + svd.delete_ptr = false; + + if (result && result->valid()) + { + return result; + } + + details::free_node(node_allocator_, result); + + set_error(make_error( + parser_error::e_synthesis, + current_token(), + "ERR174 - Failed to generate initialisation node for vector: " + vec_name, + exprtk_error_location)); + + return error_node(); + } + + #ifndef exprtk_disable_string_capabilities + inline expression_node_ptr parse_define_string_statement(const std::string& str_name, expression_node_ptr initialisation_expression) + { + stringvar_node_t* str_node = reinterpret_cast(0); + + scope_element& se = sem_.get_element(str_name); + + if (se.name == str_name) + { + if (se.active) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR175 - Illegal redefinition of local variable: '" + str_name + "'", + exprtk_error_location)); + + free_node(node_allocator_, initialisation_expression); + + return error_node(); + } + else if (scope_element::e_string == se.type) + { + str_node = se.str_node; + se.active = true; + se.depth = state_.scope_depth; + se.ref_count++; + } + } + + if (0 == str_node) + { + scope_element nse; + nse.name = str_name; + nse.active = true; + nse.ref_count = 1; + nse.type = scope_element::e_string; + nse.depth = state_.scope_depth; + nse.data = new std::string; + nse.str_node = new stringvar_node_t(*reinterpret_cast(nse.data)); + + if (!sem_.add_element(nse)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR176 - Failed to add new local string variable '" + str_name + "' to SEM", + exprtk_error_location)); + + free_node(node_allocator_, initialisation_expression); + + sem_.free_element(nse); + + return error_node(); + } + + assert(sem_.total_local_symb_size_bytes() <= settings().max_total_local_symbol_size_bytes()); + + str_node = nse.str_node; + + exprtk_debug(("parse_define_string_statement() - INFO - Added new local string variable: %s\n", nse.name.c_str())); + } + + lodge_symbol(str_name, e_st_local_string); + + state_.activate_side_effect("parse_define_string_statement()"); + + expression_node_ptr branch[2] = {0}; + + branch[0] = str_node; + branch[1] = initialisation_expression; + + return expression_generator_(details::e_assign,branch); + } + #else + inline expression_node_ptr parse_define_string_statement(const std::string&, expression_node_ptr) + { + return error_node(); + } + #endif + + inline bool local_variable_is_shadowed(const std::string& symbol) + { + const scope_element& se = sem_.get_element(symbol); + return (se.name == symbol) && se.active; + } + + inline expression_node_ptr parse_define_var_statement() + { + if (settings_.vardef_disabled()) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR177 - Illegal variable definition", + exprtk_error_location)); + + return error_node(); + } + else if (!details::imatch(current_token().value,"var")) + { + return error_node(); + } + else + next_token(); + + const std::string var_name = current_token().value; + + expression_node_ptr initialisation_expression = error_node(); + + if (!token_is(token_t::e_symbol)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR178 - Expected a symbol for variable definition", + exprtk_error_location)); + + return error_node(); + } + else if (details::is_reserved_symbol(var_name)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR179 - Illegal redefinition of reserved keyword: '" + var_name + "'", + exprtk_error_location)); + + return error_node(); + } + else if (symtab_store_.symbol_exists(var_name)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR180 - Illegal redefinition of variable '" + var_name + "'", + exprtk_error_location)); + + return error_node(); + } + else if (local_variable_is_shadowed(var_name)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR181 - Illegal redefinition of local variable: '" + var_name + "'", + exprtk_error_location)); + + return error_node(); + } + else if (token_is(token_t::e_lsqrbracket,prsrhlpr_t::e_hold)) + { + return parse_define_vector_statement(var_name); + } + else if (token_is(token_t::e_lcrlbracket,prsrhlpr_t::e_hold)) + { + return parse_uninitialised_var_statement(var_name); + } + else if (token_is(token_t::e_assign)) + { + if (0 == (initialisation_expression = parse_expression())) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR182 - Failed to parse initialisation expression for variable '" + var_name + "'", + exprtk_error_location)); + + return error_node(); + } + } + + if ( + !token_is(token_t::e_rbracket , prsrhlpr_t::e_hold) && + !token_is(token_t::e_rcrlbracket, prsrhlpr_t::e_hold) && + !token_is(token_t::e_rsqrbracket, prsrhlpr_t::e_hold) + ) + { + if (!token_is(token_t::e_eof,prsrhlpr_t::e_hold)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR183 - Expected ';' after variable '" + var_name + "' definition", + exprtk_error_location)); + + free_node(node_allocator_, initialisation_expression); + + return error_node(); + } + } + + if ( + (0 != initialisation_expression) && + details::is_generally_string_node(initialisation_expression) + ) + { + return parse_define_string_statement(var_name,initialisation_expression); + } + + expression_node_ptr var_node = reinterpret_cast(0); + + scope_element& se = sem_.get_element(var_name); + + if (se.name == var_name) + { + if (se.active) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR184 - Illegal redefinition of local variable: '" + var_name + "'", + exprtk_error_location)); + + free_node(node_allocator_, initialisation_expression); + + return error_node(); + } + else if (scope_element::e_variable == se.type) + { + var_node = se.var_node; + se.active = true; + se.depth = state_.scope_depth; + se.ref_count++; + } + } + + if (0 == var_node) + { + const std::size_t predicted_total_lclsymb_size = sizeof(T) + sem_.total_local_symb_size_bytes(); + + if (predicted_total_lclsymb_size > settings().max_total_local_symbol_size_bytes()) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR185 - Adding variable '" + var_name + "' " + "will exceed max total local symbol size of: " + details::to_str(settings().max_total_local_symbol_size_bytes()) + " bytes, " + "current total size: " + details::to_str(sem_.total_local_symb_size_bytes()) + " bytes", + exprtk_error_location)); + + free_node(node_allocator_, initialisation_expression); + + return error_node(); + } + + scope_element nse; + nse.name = var_name; + nse.active = true; + nse.ref_count = 1; + nse.type = scope_element::e_variable; + nse.depth = state_.scope_depth; + nse.data = new T(T(0)); + nse.var_node = node_allocator_.allocate(*reinterpret_cast(nse.data)); + + if (!sem_.add_element(nse)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR186 - Failed to add new local variable '" + var_name + "' to SEM", + exprtk_error_location)); + + free_node(node_allocator_, initialisation_expression); + + sem_.free_element(nse); + + return error_node(); + } + + assert(sem_.total_local_symb_size_bytes() <= settings().max_total_local_symbol_size_bytes()); + + var_node = nse.var_node; + + exprtk_debug(("parse_define_var_statement() - INFO - Added new local variable: %s\n", nse.name.c_str())); + } + + state_.activate_side_effect("parse_define_var_statement()"); + + lodge_symbol(var_name, e_st_local_variable); + + expression_node_ptr branch[2] = {0}; + + branch[0] = var_node; + branch[1] = initialisation_expression ? initialisation_expression : expression_generator_(T(0)); + + return expression_generator_(details::e_assign,branch); + } + + inline expression_node_ptr parse_define_constvar_statement() + { + if (settings_.vardef_disabled()) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR187 - Illegal const variable definition", + exprtk_error_location)); + + return error_node(); + } + else if (!token_is("const")) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR188 - Expected 'const' keyword for const-variable definition", + exprtk_error_location)); + + return error_node(); + } + else if (!token_is("var")) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR189 - Expected 'var' keyword for const-variable definition", + exprtk_error_location)); + + return error_node(); + } + + const std::string var_name = current_token().value; + + expression_node_ptr initialisation_expression = error_node(); + + if (!token_is(token_t::e_symbol)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR190 - Expected a symbol for const-variable definition", + exprtk_error_location)); + + return error_node(); + } + else if (details::is_reserved_symbol(var_name)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR191 - Illegal redefinition of reserved keyword: '" + var_name + "'", + exprtk_error_location)); + + return error_node(); + } + else if (symtab_store_.symbol_exists(var_name)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR192 - Illegal redefinition of variable '" + var_name + "'", + exprtk_error_location)); + + return error_node(); + } + else if (local_variable_is_shadowed(var_name)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR193 - Illegal redefinition of local variable: '" + var_name + "'", + exprtk_error_location)); + + return error_node(); + } + else if (!token_is(token_t::e_assign)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR194 - Expected assignment operator after const-variable: '" + var_name + "' definition", + exprtk_error_location)); + + return error_node(); + } + else if (0 == (initialisation_expression = parse_expression())) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR195 - Failed to parse initialisation expression for const-variable: '" + var_name + "'", + exprtk_error_location)); + + return error_node(); + } + + if (!details::is_literal_node(initialisation_expression)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR196 - initialisation expression for const-variable: '" + var_name + "' must be a constant/literal", + exprtk_error_location)); + + free_node(node_allocator_, initialisation_expression); + + return error_node(); + } + + assert(initialisation_expression); + + const T init_value = initialisation_expression->value(); + + free_node(node_allocator_, initialisation_expression); + + expression_node_ptr var_node = reinterpret_cast(0); + + scope_element& se = sem_.get_element(var_name); + + if (se.name == var_name) + { + if (se.active) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR197 - Illegal redefinition of local variable: '" + var_name + "'", + exprtk_error_location)); + + return error_node(); + } + else if (scope_element::e_literal == se.type) + { + var_node = se.var_node; + se.active = true; + se.depth = state_.scope_depth; + se.ref_count++; + } + } + + if (0 == var_node) + { + const std::size_t predicted_total_lclsymb_size = sizeof(T) + sem_.total_local_symb_size_bytes(); + + if (predicted_total_lclsymb_size > settings().max_total_local_symbol_size_bytes()) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR198 - Adding variable '" + var_name + "' " + "will exceed max total local symbol size of: " + details::to_str(settings().max_total_local_symbol_size_bytes()) + " bytes, " + "current total size: " + details::to_str(sem_.total_local_symb_size_bytes()) + " bytes", + exprtk_error_location)); + + return error_node(); + } + + scope_element nse; + nse.name = var_name; + nse.active = true; + nse.ref_count = 1; + nse.type = scope_element::e_literal; + nse.depth = state_.scope_depth; + nse.data = 0; + nse.var_node = node_allocator_.allocate(init_value); + + if (!sem_.add_element(nse)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR199 - Failed to add new local const-variable '" + var_name + "' to SEM", + exprtk_error_location)); + + sem_.free_element(nse); + + return error_node(); + } + + assert(sem_.total_local_symb_size_bytes() <= settings().max_total_local_symbol_size_bytes()); + + var_node = nse.var_node; + + exprtk_debug(("parse_define_constvar_statement() - INFO - Added new local const-variable: %s\n", nse.name.c_str())); + } + + state_.activate_side_effect("parse_define_constvar_statement()"); + + lodge_symbol(var_name, e_st_local_variable); + + return expression_generator_(var_node->value()); + } + + inline expression_node_ptr parse_uninitialised_var_statement(const std::string& var_name) + { + if ( + !token_is(token_t::e_lcrlbracket) || + !token_is(token_t::e_rcrlbracket) + ) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR200 - Expected a '{}' for uninitialised var definition", + exprtk_error_location)); + + return error_node(); + } + else if (!token_is(token_t::e_eof,prsrhlpr_t::e_hold)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR201 - Expected ';' after uninitialised variable definition", + exprtk_error_location)); + + return error_node(); + } + + expression_node_ptr var_node = reinterpret_cast(0); + + scope_element& se = sem_.get_element(var_name); + + if (se.name == var_name) + { + if (se.active) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR202 - Illegal redefinition of local variable: '" + var_name + "'", + exprtk_error_location)); + + return error_node(); + } + else if (scope_element::e_variable == se.type) + { + var_node = se.var_node; + se.active = true; + se.ref_count++; + } + } + + if (0 == var_node) + { + const std::size_t predicted_total_lclsymb_size = sizeof(T) + sem_.total_local_symb_size_bytes(); + + if (predicted_total_lclsymb_size > settings().max_total_local_symbol_size_bytes()) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR203 - Adding variable '" + var_name + "' " + "will exceed max total local symbol size of: " + details::to_str(settings().max_total_local_symbol_size_bytes()) + " bytes, " + "current total size: " + details::to_str(sem_.total_local_symb_size_bytes()) + " bytes", + exprtk_error_location)); + + return error_node(); + } + + scope_element nse; + nse.name = var_name; + nse.active = true; + nse.ref_count = 1; + nse.type = scope_element::e_variable; + nse.depth = state_.scope_depth; + nse.ip_index = sem_.next_ip_index(); + nse.data = new T(T(0)); + nse.var_node = node_allocator_.allocate(*reinterpret_cast(nse.data)); + + if (!sem_.add_element(nse)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR204 - Failed to add new local variable '" + var_name + "' to SEM", + exprtk_error_location)); + + sem_.free_element(nse); + + return error_node(); + } + + assert(sem_.total_local_symb_size_bytes() <= settings().max_total_local_symbol_size_bytes()); + + exprtk_debug(("parse_uninitialised_var_statement() - INFO - Added new local variable: %s\n", + nse.name.c_str())); + } + + lodge_symbol(var_name, e_st_local_variable); + + state_.activate_side_effect("parse_uninitialised_var_statement()"); + + return expression_generator_(T(0)); + } + + inline expression_node_ptr parse_swap_statement() + { + if (!details::imatch(current_token().value,"swap")) + { + return error_node(); + } + else + next_token(); + + if (!token_is(token_t::e_lbracket)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR205 - Expected '(' at start of swap statement", + exprtk_error_location)); + + return error_node(); + } + + expression_node_ptr variable0 = error_node(); + expression_node_ptr variable1 = error_node(); + + bool variable0_generated = false; + bool variable1_generated = false; + + const std::string var0_name = current_token().value; + + if (!token_is(token_t::e_symbol,prsrhlpr_t::e_hold)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR206 - Expected a symbol for variable or vector element definition", + exprtk_error_location)); + + return error_node(); + } + else if (peek_token_is(token_t::e_lsqrbracket)) + { + if (0 == (variable0 = parse_vector())) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR207 - First parameter to swap is an invalid vector element: '" + var0_name + "'", + exprtk_error_location)); + + return error_node(); + } + + variable0_generated = true; + } + else + { + if (symtab_store_.is_variable(var0_name)) + { + variable0 = symtab_store_.get_variable(var0_name); + } + + const scope_element& se = sem_.get_element(var0_name); + + if ( + (se.active) && + (se.name == var0_name) && + (scope_element::e_variable == se.type) + ) + { + variable0 = se.var_node; + } + + lodge_symbol(var0_name, e_st_variable); + + if (0 == variable0) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR208 - First parameter to swap is an invalid variable: '" + var0_name + "'", + exprtk_error_location)); + + return error_node(); + } + else + next_token(); + } + + if (!token_is(token_t::e_comma)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR209 - Expected ',' between parameters to swap", + exprtk_error_location)); + + if (variable0_generated) + { + free_node(node_allocator_, variable0); + } + + return error_node(); + } + + const std::string var1_name = current_token().value; + + if (!token_is(token_t::e_symbol,prsrhlpr_t::e_hold)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR210 - Expected a symbol for variable or vector element definition", + exprtk_error_location)); + + if (variable0_generated) + { + free_node(node_allocator_, variable0); + } + + return error_node(); + } + else if (peek_token_is(token_t::e_lsqrbracket)) + { + if (0 == (variable1 = parse_vector())) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR211 - Second parameter to swap is an invalid vector element: '" + var1_name + "'", + exprtk_error_location)); + + if (variable0_generated) + { + free_node(node_allocator_, variable0); + } + + return error_node(); + } + + variable1_generated = true; + } + else + { + if (symtab_store_.is_variable(var1_name)) + { + variable1 = symtab_store_.get_variable(var1_name); + } + + const scope_element& se = sem_.get_element(var1_name); + + if ( + (se.active) && + (se.name == var1_name) && + (scope_element::e_variable == se.type) + ) + { + variable1 = se.var_node; + } + + lodge_symbol(var1_name, e_st_variable); + + if (0 == variable1) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR212 - Second parameter to swap is an invalid variable: '" + var1_name + "'", + exprtk_error_location)); + + if (variable0_generated) + { + free_node(node_allocator_, variable0); + } + + return error_node(); + } + else + next_token(); + } + + if (!token_is(token_t::e_rbracket)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR213 - Expected ')' at end of swap statement", + exprtk_error_location)); + + if (variable0_generated) + { + free_node(node_allocator_, variable0); + } + + if (variable1_generated) + { + free_node(node_allocator_, variable1); + } + + return error_node(); + } + + typedef details::variable_node* variable_node_ptr; + + variable_node_ptr v0 = variable_node_ptr(0); + variable_node_ptr v1 = variable_node_ptr(0); + + expression_node_ptr result = error_node(); + + if ( + (0 != (v0 = dynamic_cast(variable0))) && + (0 != (v1 = dynamic_cast(variable1))) + ) + { + result = node_allocator_.allocate >(v0, v1); + + if (variable0_generated) + { + free_node(node_allocator_, variable0); + } + + if (variable1_generated) + { + free_node(node_allocator_, variable1); + } + } + else + result = node_allocator_.allocate > + (variable0, variable1); + + state_.activate_side_effect("parse_swap_statement()"); + + return result; + } + + #ifndef exprtk_disable_return_statement + inline expression_node_ptr parse_return_statement() + { + if (state_.parsing_return_stmt) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR214 - Return call within a return call is not allowed", + exprtk_error_location)); + + return error_node(); + } + + scoped_bool_negator sbn(state_.parsing_return_stmt); + + std::vector arg_list; + + scoped_vec_delete svd((*this), arg_list); + + if (!details::imatch(current_token().value,"return")) + { + return error_node(); + } + else + next_token(); + + if (!token_is(token_t::e_lsqrbracket)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR215 - Expected '[' at start of return statement", + exprtk_error_location)); + + return error_node(); + } + else if (!token_is(token_t::e_rsqrbracket)) + { + for ( ; ; ) + { + expression_node_ptr arg = parse_expression(); + + if (0 == arg) + return error_node(); + + arg_list.push_back(arg); + + if (token_is(token_t::e_rsqrbracket)) + break; + else if (!token_is(token_t::e_comma)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR216 - Expected ',' between values during call to return", + exprtk_error_location)); + + return error_node(); + } + } + } + else if (settings_.zero_return_disabled()) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR217 - Zero parameter return statement not allowed", + exprtk_error_location)); + + return error_node(); + } + + const lexer::token prev_token = current_token(); + + if (token_is(token_t::e_rsqrbracket)) + { + if (!arg_list.empty()) + { + set_error(make_error( + parser_error::e_syntax, + prev_token, + "ERR218 - Invalid ']' found during return call", + exprtk_error_location)); + + return error_node(); + } + } + + std::string ret_param_type_list; + + for (std::size_t i = 0; i < arg_list.size(); ++i) + { + if (0 == arg_list[i]) + return error_node(); + else if (is_ivector_node(arg_list[i])) + ret_param_type_list += 'V'; + else if (is_generally_string_node(arg_list[i])) + ret_param_type_list += 'S'; + else + ret_param_type_list += 'T'; + } + + dec_.retparam_list_.push_back(ret_param_type_list); + + expression_node_ptr result = expression_generator_.return_call(arg_list); + + svd.delete_ptr = (0 == result); + + state_.return_stmt_present = true; + + state_.activate_side_effect("parse_return_statement()"); + + return result; + } + #else + inline expression_node_ptr parse_return_statement() + { + return error_node(); + } + #endif + + inline expression_node_ptr parse_assert_statement() + { + assert(details::imatch(current_token().value, "assert")); + + if (state_.parsing_assert_stmt) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR219 - Assert statement within an assert statement is not allowed", + exprtk_error_location)); + + return error_node(); + } + + scoped_bool_negator sbn(state_.parsing_assert_stmt); + + next_token(); + + std::vector assert_arg_list(3, error_node()); + scoped_vec_delete svd((*this), assert_arg_list); + + expression_node_ptr& assert_condition = assert_arg_list[0]; + expression_node_ptr& assert_message = assert_arg_list[1]; + expression_node_ptr& assert_id = assert_arg_list[2]; + + if (!token_is(token_t::e_lbracket)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR220 - Expected '(' at start of assert statement", + exprtk_error_location)); + + return error_node(); + } + + const token_t start_token = current_token(); + + // Parse the assert condition + if (0 == (assert_condition = parse_expression())) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR221 - Failed to parse condition for assert statement", + exprtk_error_location)); + + return error_node(); + } + + const token_t end_token = current_token(); + + if (!token_is(token_t::e_rbracket)) + { + if (!token_is(token_t::e_comma)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR222 - Expected ',' between condition and message for assert statement", + exprtk_error_location)); + + return error_node(); + } + // Parse the assert message + else if ( + (0 == (assert_message = parse_expression())) || + !details::is_generally_string_node(assert_message) + ) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR223 - " + + (assert_message ? + std::string("Expected string for assert message") : + std::string("Failed to parse message for assert statement")), + exprtk_error_location)); + + return error_node(); + } + else if (!token_is(token_t::e_rbracket)) + { + if (!token_is(token_t::e_comma)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR224 - Expected ',' between message and ID for assert statement", + exprtk_error_location)); + + return error_node(); + } + // Parse assert ID + else if ( + (0 == (assert_id = parse_expression())) || + !details::is_const_string_node(assert_id) + ) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR225 - " + + (assert_id ? + std::string("Expected literal string for assert ID") : + std::string("Failed to parse string for assert ID")), + exprtk_error_location)); + + return error_node(); + } + else if (!token_is(token_t::e_rbracket)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR226 - Expected ')' at start of assert statement", + exprtk_error_location)); + + return error_node(); + } + } + } + + exprtk::assert_check::assert_context context; + context.condition = lexer().substr(start_token.position, end_token.position); + context.offet = start_token.position; + + if (0 == assert_check_) + { + exprtk_debug(("parse_assert_statement() - assert functionality is disabled. assert condition: %s\n", + context.condition.c_str())); + + return new details::null_node(); + } + + #ifndef exprtk_disable_string_capabilities + if (assert_message && details::is_const_string_node(assert_message)) + { + context.message = dynamic_cast*>(assert_message)->str(); + } + + if (assert_id && details::is_const_string_node(assert_id)) + { + context.id = dynamic_cast*>(assert_id)->str(); + + if (assert_ids_.end() != assert_ids_.find(context.id)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR227 - Duplicate assert ID: " + context.id, + exprtk_error_location)); + + return error_node(); + } + + assert_ids_.insert(context.id); + free_node(node_allocator_, assert_id); + } + #endif + + expression_node_ptr result_node = + expression_generator_.assert_call( + assert_condition, + assert_message, + context); + + exprtk_debug(("parse_assert_statement() - assert condition: [%s]\n", context.condition.c_str() )); + exprtk_debug(("parse_assert_statement() - assert message: [%s]\n", context.message .c_str() )); + exprtk_debug(("parse_assert_statement() - assert id: [%s]\n", context.id .c_str() )); + exprtk_debug(("parse_assert_statement() - assert offset: [%d]\n", static_cast(context.offet))); + + if (0 == result_node) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR228 - Failed to synthesize assert", + exprtk_error_location)); + + return error_node(); + } + + svd.delete_ptr = false; + return result_node; + } + + inline bool post_variable_process(const std::string& symbol) + { + if ( + peek_token_is(token_t::e_lbracket ) || + peek_token_is(token_t::e_lcrlbracket) || + peek_token_is(token_t::e_lsqrbracket) + ) + { + if (!settings_.commutative_check_enabled()) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR229 - Invalid sequence of variable '" + symbol + "' and bracket", + exprtk_error_location)); + + return false; + } + + lexer().insert_front(token_t::e_mul); + } + + return true; + } + + inline bool post_bracket_process(const typename token_t::token_type& token, expression_node_ptr& branch) + { + bool implied_mul = false; + + if (details::is_generally_string_node(branch)) + return true; + + if (details::is_ivector_node(branch)) + return true; + + const lexer::parser_helper::token_advance_mode hold = prsrhlpr_t::e_hold; + + switch (token) + { + case token_t::e_lcrlbracket : implied_mul = token_is(token_t::e_lbracket , hold) || + token_is(token_t::e_lcrlbracket, hold) || + token_is(token_t::e_lsqrbracket, hold) ; + break; + + case token_t::e_lbracket : implied_mul = token_is(token_t::e_lbracket , hold) || + token_is(token_t::e_lcrlbracket, hold) || + token_is(token_t::e_lsqrbracket, hold) ; + break; + + case token_t::e_lsqrbracket : implied_mul = token_is(token_t::e_lbracket , hold) || + token_is(token_t::e_lcrlbracket, hold) || + token_is(token_t::e_lsqrbracket, hold) ; + break; + + default : return true; + } + + if (implied_mul) + { + if (!settings_.commutative_check_enabled()) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR230 - Invalid sequence of brackets", + exprtk_error_location)); + + return false; + } + else if (token_t::e_eof != current_token().type) + { + lexer().insert_front(current_token().type); + lexer().insert_front(token_t::e_mul); + next_token(); + } + } + + return true; + } + + typedef typename interval_container_t::interval_t interval_t; + typedef interval_container_t immutable_memory_map_t; + typedef std::map immutable_symtok_map_t; + + inline interval_t make_memory_range(const T& t) + { + const T* begin = reinterpret_cast(&t); + const T* end = begin + 1; + return interval_t(begin, end); + } + + inline interval_t make_memory_range(const T* begin, const std::size_t size) + { + return interval_t(begin, begin + size); + } + + inline interval_t make_memory_range(details::char_cptr begin, const std::size_t size) + { + return interval_t(begin, begin + size); + } + + void lodge_immutable_symbol(const lexer::token& token, const interval_t interval) + { + immutable_memory_map_.add_interval(interval); + immutable_symtok_map_[interval] = token; + } + + inline expression_node_ptr parse_symtab_symbol() + { + const std::string symbol = current_token().value; + + // Are we dealing with a variable or a special constant? + typedef typename symtab_store::variable_context var_ctxt_t; + var_ctxt_t var_ctx = symtab_store_.get_variable_context(symbol); + + if (var_ctx.variable) + { + assert(var_ctx.symbol_table); + + expression_node_ptr result_variable = var_ctx.variable; + + if (symtab_store_.is_constant_node(symbol)) + { + result_variable = expression_generator_(var_ctx.variable->value()); + } + else if (symbol_table_t::e_immutable == var_ctx.symbol_table->mutability()) + { + lodge_immutable_symbol(current_token(), make_memory_range(var_ctx.variable->ref())); + result_variable = var_ctx.variable; + } + + if (!post_variable_process(symbol)) + return error_node(); + + lodge_symbol(symbol, e_st_variable); + + next_token(); + + return result_variable; + } + + // Are we dealing with a locally defined variable, vector or string? + if (!sem_.empty()) + { + scope_element& se = sem_.get_active_element(symbol); + + if (se.active && details::imatch(se.name, symbol)) + { + if ( + (scope_element::e_variable == se.type) || + (scope_element::e_literal == se.type) + ) + { + se.active = true; + lodge_symbol(symbol, e_st_local_variable); + + if (!post_variable_process(symbol)) + return error_node(); + + next_token(); + + return (scope_element::e_variable == se.type) ? + se.var_node : + expression_generator_(se.var_node->value()); + } + else if (scope_element::e_vector == se.type) + { + return parse_vector(); + } + #ifndef exprtk_disable_string_capabilities + else if (scope_element::e_string == se.type) + { + return parse_string(); + } + #endif + } + } + + #ifndef exprtk_disable_string_capabilities + // Are we dealing with a string variable? + if (symtab_store_.is_stringvar(symbol)) + { + return parse_string(); + } + #endif + + { + // Are we dealing with a function? + ifunction* function = symtab_store_.get_function(symbol); + + if (function) + { + lodge_symbol(symbol, e_st_function); + + expression_node_ptr func_node = + parse_function_invocation(function,symbol); + + if (func_node) + return func_node; + else + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR231 - Failed to generate node for function: '" + symbol + "'", + exprtk_error_location)); + + return error_node(); + } + } + } + + { + // Are we dealing with a vararg function? + ivararg_function* vararg_function = symtab_store_.get_vararg_function(symbol); + + if (vararg_function) + { + lodge_symbol(symbol, e_st_function); + + expression_node_ptr vararg_func_node = + parse_vararg_function_call(vararg_function, symbol); + + if (vararg_func_node) + return vararg_func_node; + else + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR232 - Failed to generate node for vararg function: '" + symbol + "'", + exprtk_error_location)); + + return error_node(); + } + } + } + + { + // Are we dealing with a vararg generic function? + igeneric_function* generic_function = symtab_store_.get_generic_function(symbol); + + if (generic_function) + { + lodge_symbol(symbol, e_st_function); + + expression_node_ptr genericfunc_node = + parse_generic_function_call(generic_function, symbol); + + if (genericfunc_node) + return genericfunc_node; + else + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR233 - Failed to generate node for generic function: '" + symbol + "'", + exprtk_error_location)); + + return error_node(); + } + } + } + + #ifndef exprtk_disable_string_capabilities + { + // Are we dealing with a vararg string returning function? + igeneric_function* string_function = symtab_store_.get_string_function(symbol); + + if (string_function) + { + lodge_symbol(symbol, e_st_function); + + expression_node_ptr stringfunc_node = + parse_string_function_call(string_function, symbol); + + if (stringfunc_node) + return stringfunc_node; + else + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR234 - Failed to generate node for string function: '" + symbol + "'", + exprtk_error_location)); + + return error_node(); + } + } + } + + { + // Are we dealing with a vararg overloaded scalar/string returning function? + igeneric_function* overload_function = symtab_store_.get_overload_function(symbol); + + if (overload_function) + { + lodge_symbol(symbol, e_st_function); + + expression_node_ptr overloadfunc_node = + parse_overload_function_call(overload_function, symbol); + + if (overloadfunc_node) + return overloadfunc_node; + else + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR235 - Failed to generate node for overload function: '" + symbol + "'", + exprtk_error_location)); + + return error_node(); + } + } + } + #endif + + // Are we dealing with a vector? + if (symtab_store_.is_vector(symbol)) + { + lodge_symbol(symbol, e_st_vector); + return parse_vector(); + } + + if (details::is_reserved_symbol(symbol)) + { + if ( + settings_.function_enabled(symbol) || + !details::is_base_function(symbol) + ) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR236 - Invalid use of reserved symbol '" + symbol + "'", + exprtk_error_location)); + + return error_node(); + } + } + + // Should we handle unknown symbols? + if (resolve_unknown_symbol_ && unknown_symbol_resolver_) + { + if (!(settings_.rsrvd_sym_usr_disabled() && details::is_reserved_symbol(symbol))) + { + symbol_table_t& symtab = symtab_store_.get_symbol_table(); + + std::string error_message; + + if (unknown_symbol_resolver::e_usrmode_default == unknown_symbol_resolver_->mode) + { + T default_value = T(0); + + typename unknown_symbol_resolver::usr_symbol_type usr_symbol_type = unknown_symbol_resolver::e_usr_unknown_type; + + if (unknown_symbol_resolver_->process(symbol, usr_symbol_type, default_value, error_message)) + { + bool create_result = false; + + switch (usr_symbol_type) + { + case unknown_symbol_resolver::e_usr_variable_type : + create_result = symtab.create_variable(symbol, default_value); + break; + + case unknown_symbol_resolver::e_usr_constant_type : + create_result = symtab.add_constant(symbol, default_value); + break; + + default : create_result = false; + } + + if (create_result) + { + expression_node_ptr var = symtab_store_.get_variable(symbol); + + if (var) + { + if (symtab_store_.is_constant_node(symbol)) + { + var = expression_generator_(var->value()); + } + + lodge_symbol(symbol, e_st_variable); + + if (!post_variable_process(symbol)) + return error_node(); + + next_token(); + + return var; + } + } + } + + set_error(make_error( + parser_error::e_symtab, + current_token(), + "ERR237 - Failed to create variable: '" + symbol + "'" + + (error_message.empty() ? "" : " - " + error_message), + exprtk_error_location)); + + } + else if (unknown_symbol_resolver::e_usrmode_extended == unknown_symbol_resolver_->mode) + { + if (unknown_symbol_resolver_->process(symbol, symtab, error_message)) + { + expression_node_ptr result = parse_symtab_symbol(); + + if (result) + { + return result; + } + } + + set_error(make_error( + parser_error::e_symtab, + current_token(), + "ERR238 - Failed to resolve symbol: '" + symbol + "'" + + (error_message.empty() ? "" : " - " + error_message), + exprtk_error_location)); + } + + return error_node(); + } + } + + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR239 - Undefined symbol: '" + symbol + "'", + exprtk_error_location)); + + return error_node(); + } + + inline expression_node_ptr check_block_statement_closure(expression_node_ptr expression) + { + if ( + expression && + ( + (current_token().type == token_t::e_symbol) || + (current_token().type == token_t::e_number) + ) + ) + { + free_node(node_allocator_, expression); + + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR240 - Invalid syntax '" + current_token().value + "' possible missing operator or context", + exprtk_error_location)); + + return error_node(); + } + + return expression; + } + + inline expression_node_ptr parse_symbol() + { + static const std::string symbol_if = "if" ; + static const std::string symbol_while = "while" ; + static const std::string symbol_repeat = "repeat" ; + static const std::string symbol_for = "for" ; + static const std::string symbol_switch = "switch" ; + static const std::string symbol_null = "null" ; + static const std::string symbol_break = "break" ; + static const std::string symbol_continue = "continue"; + static const std::string symbol_var = "var" ; + static const std::string symbol_const = "const" ; + static const std::string symbol_swap = "swap" ; + static const std::string symbol_return = "return" ; + static const std::string symbol_not = "not" ; + static const std::string symbol_assert = "assert" ; + + const std::string symbol = current_token().value; + + if (valid_vararg_operation(symbol)) + { + return parse_vararg_function(); + } + else if (details::imatch(symbol, symbol_not)) + { + return parse_not_statement(); + } + else if (valid_base_operation(symbol)) + { + return parse_base_operation(); + } + else if ( + details::imatch(symbol, symbol_if) && + settings_.control_struct_enabled(symbol) + ) + { + return parse_conditional_statement(); + } + else if ( + details::imatch(symbol, symbol_while) && + settings_.control_struct_enabled(symbol) + ) + { + return check_block_statement_closure(parse_while_loop()); + } + else if ( + details::imatch(symbol, symbol_repeat) && + settings_.control_struct_enabled(symbol) + ) + { + return check_block_statement_closure(parse_repeat_until_loop()); + } + else if ( + details::imatch(symbol, symbol_for) && + settings_.control_struct_enabled(symbol) + ) + { + return check_block_statement_closure(parse_for_loop()); + } + else if ( + details::imatch(symbol, symbol_switch) && + settings_.control_struct_enabled(symbol) + ) + { + return check_block_statement_closure(parse_switch_statement()); + } + else if (details::is_valid_sf_symbol(symbol)) + { + return parse_special_function(); + } + else if (details::imatch(symbol, symbol_null)) + { + return parse_null_statement(); + } + #ifndef exprtk_disable_break_continue + else if (details::imatch(symbol, symbol_break)) + { + return parse_break_statement(); + } + else if (details::imatch(symbol, symbol_continue)) + { + return parse_continue_statement(); + } + #endif + else if (details::imatch(symbol, symbol_var)) + { + return parse_define_var_statement(); + } + else if (details::imatch(symbol, symbol_const)) + { + return parse_define_constvar_statement(); + } + else if (details::imatch(symbol, symbol_swap)) + { + return parse_swap_statement(); + } + #ifndef exprtk_disable_return_statement + else if ( + details::imatch(symbol, symbol_return) && + settings_.control_struct_enabled(symbol) + ) + { + return check_block_statement_closure(parse_return_statement()); + } + #endif + else if (details::imatch(symbol, symbol_assert)) + { + return parse_assert_statement(); + } + else if (symtab_store_.valid() || !sem_.empty()) + { + return parse_symtab_symbol(); + } + else + { + set_error(make_error( + parser_error::e_symtab, + current_token(), + "ERR241 - Unknown variable or function encountered. Symbol table(s) " + "is either invalid or does not contain symbol: '" + symbol + "'", + exprtk_error_location)); + + return error_node(); + } + } + + inline expression_node_ptr parse_branch(precedence_level precedence = e_level00) + { + stack_limit_handler slh(*this); + + if (!slh) + { + return error_node(); + } + + expression_node_ptr branch = error_node(); + + if (token_t::e_number == current_token().type) + { + T numeric_value = T(0); + + if (details::string_to_real(current_token().value, numeric_value)) + { + expression_node_ptr literal_exp = expression_generator_(numeric_value); + + if (0 == literal_exp) + { + set_error(make_error( + parser_error::e_numeric, + current_token(), + "ERR242 - Failed generate node for scalar: '" + current_token().value + "'", + exprtk_error_location)); + + return error_node(); + } + + next_token(); + branch = literal_exp; + } + else + { + set_error(make_error( + parser_error::e_numeric, + current_token(), + "ERR243 - Failed to convert '" + current_token().value + "' to a number", + exprtk_error_location)); + + return error_node(); + } + } + else if (token_t::e_symbol == current_token().type) + { + branch = parse_symbol(); + } + #ifndef exprtk_disable_string_capabilities + else if (token_t::e_string == current_token().type) + { + branch = parse_const_string(); + } + #endif + else if (token_t::e_lbracket == current_token().type) + { + next_token(); + + if (0 == (branch = parse_expression())) + { + return error_node(); + } + + token_is(token_t::e_eof); + + if (!token_is(token_t::e_rbracket)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR244 - Expected ')' instead of: '" + current_token().value + "'", + exprtk_error_location)); + + details::free_node(node_allocator_, branch); + + return error_node(); + } + else if (!post_bracket_process(token_t::e_lbracket,branch)) + { + details::free_node(node_allocator_, branch); + + return error_node(); + } + + parse_pending_vector_index_operator(branch); + } + else if (token_t::e_lsqrbracket == current_token().type) + { + next_token(); + + if (0 == (branch = parse_expression())) + return error_node(); + else if (!token_is(token_t::e_rsqrbracket)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR245 - Expected ']' instead of: '" + current_token().value + "'", + exprtk_error_location)); + + details::free_node(node_allocator_, branch); + + return error_node(); + } + else if (!post_bracket_process(token_t::e_lsqrbracket,branch)) + { + details::free_node(node_allocator_, branch); + + return error_node(); + } + } + else if (token_t::e_lcrlbracket == current_token().type) + { + next_token(); + + if (0 == (branch = parse_expression())) + return error_node(); + else if (!token_is(token_t::e_rcrlbracket)) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR246 - Expected '}' instead of: '" + current_token().value + "'", + exprtk_error_location)); + + details::free_node(node_allocator_, branch); + + return error_node(); + } + else if (!post_bracket_process(token_t::e_lcrlbracket,branch)) + { + details::free_node(node_allocator_, branch); + + return error_node(); + } + } + else if (token_t::e_sub == current_token().type) + { + next_token(); + branch = parse_expression(e_level11); + + if ( + branch && + !( + details::is_neg_unary_node (branch) && + simplify_unary_negation_branch(branch) + ) + ) + { + expression_node_ptr result = expression_generator_(details::e_neg,branch); + + if (0 == result) + { + details::free_node(node_allocator_, branch); + + return error_node(); + } + else + branch = result; + } + } + else if (token_t::e_add == current_token().type) + { + next_token(); + branch = parse_expression(e_level13); + } + else if (token_t::e_eof == current_token().type) + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR247 - Premature end of expression[1]", + exprtk_error_location)); + + return error_node(); + } + else + { + set_error(make_error( + parser_error::e_syntax, + current_token(), + "ERR248 - Premature end of expression[2]", + exprtk_error_location)); + + return error_node(); + } + + if ( + branch && + (e_level00 == precedence) && + token_is(token_t::e_ternary,prsrhlpr_t::e_hold) + ) + { + branch = parse_ternary_conditional_statement(branch); + } + + parse_pending_string_rangesize(branch); + + return branch; + } + + template + class expression_generator + { + public: + + typedef details::expression_node* expression_node_ptr; + typedef expression_node_ptr (*synthesize_functor_t)(expression_generator&, const details::operator_type& operation, expression_node_ptr (&branch)[2]); + typedef std::map synthesize_map_t; + typedef typename exprtk::parser parser_t; + typedef const Type& vtype; + typedef const Type ctype; + + inline void init_synthesize_map() + { + #ifndef exprtk_disable_enhanced_features + synthesize_map_["(v)o(v)"] = synthesize_vov_expression::process; + synthesize_map_["(c)o(v)"] = synthesize_cov_expression::process; + synthesize_map_["(v)o(c)"] = synthesize_voc_expression::process; + + #define register_synthezier(S) \ + synthesize_map_[S ::node_type::id()] = S ::process; \ + + register_synthezier(synthesize_vovov_expression0) + register_synthezier(synthesize_vovov_expression1) + register_synthezier(synthesize_vovoc_expression0) + register_synthezier(synthesize_vovoc_expression1) + register_synthezier(synthesize_vocov_expression0) + register_synthezier(synthesize_vocov_expression1) + register_synthezier(synthesize_covov_expression0) + register_synthezier(synthesize_covov_expression1) + register_synthezier(synthesize_covoc_expression0) + register_synthezier(synthesize_covoc_expression1) + register_synthezier(synthesize_cocov_expression1) + register_synthezier(synthesize_vococ_expression0) + + register_synthezier(synthesize_vovovov_expression0) + register_synthezier(synthesize_vovovoc_expression0) + register_synthezier(synthesize_vovocov_expression0) + register_synthezier(synthesize_vocovov_expression0) + register_synthezier(synthesize_covovov_expression0) + register_synthezier(synthesize_covocov_expression0) + register_synthezier(synthesize_vocovoc_expression0) + register_synthezier(synthesize_covovoc_expression0) + register_synthezier(synthesize_vococov_expression0) + + register_synthezier(synthesize_vovovov_expression1) + register_synthezier(synthesize_vovovoc_expression1) + register_synthezier(synthesize_vovocov_expression1) + register_synthezier(synthesize_vocovov_expression1) + register_synthezier(synthesize_covovov_expression1) + register_synthezier(synthesize_covocov_expression1) + register_synthezier(synthesize_vocovoc_expression1) + register_synthezier(synthesize_covovoc_expression1) + register_synthezier(synthesize_vococov_expression1) + + register_synthezier(synthesize_vovovov_expression2) + register_synthezier(synthesize_vovovoc_expression2) + register_synthezier(synthesize_vovocov_expression2) + register_synthezier(synthesize_vocovov_expression2) + register_synthezier(synthesize_covovov_expression2) + register_synthezier(synthesize_covocov_expression2) + register_synthezier(synthesize_vocovoc_expression2) + register_synthezier(synthesize_covovoc_expression2) + + register_synthezier(synthesize_vovovov_expression3) + register_synthezier(synthesize_vovovoc_expression3) + register_synthezier(synthesize_vovocov_expression3) + register_synthezier(synthesize_vocovov_expression3) + register_synthezier(synthesize_covovov_expression3) + register_synthezier(synthesize_covocov_expression3) + register_synthezier(synthesize_vocovoc_expression3) + register_synthezier(synthesize_covovoc_expression3) + register_synthezier(synthesize_vococov_expression3) + + register_synthezier(synthesize_vovovov_expression4) + register_synthezier(synthesize_vovovoc_expression4) + register_synthezier(synthesize_vovocov_expression4) + register_synthezier(synthesize_vocovov_expression4) + register_synthezier(synthesize_covovov_expression4) + register_synthezier(synthesize_covocov_expression4) + register_synthezier(synthesize_vocovoc_expression4) + register_synthezier(synthesize_covovoc_expression4) + + #undef register_synthezier + #endif + } + + inline void set_parser(parser_t& p) + { + parser_ = &p; + } + + inline void set_uom(unary_op_map_t& unary_op_map) + { + unary_op_map_ = &unary_op_map; + } + + inline void set_bom(binary_op_map_t& binary_op_map) + { + binary_op_map_ = &binary_op_map; + } + + inline void set_ibom(inv_binary_op_map_t& inv_binary_op_map) + { + inv_binary_op_map_ = &inv_binary_op_map; + } + + inline void set_sf3m(sf3_map_t& sf3_map) + { + sf3_map_ = &sf3_map; + } + + inline void set_sf4m(sf4_map_t& sf4_map) + { + sf4_map_ = &sf4_map; + } + + inline void set_allocator(details::node_allocator& na) + { + node_allocator_ = &na; + } + + inline void set_strength_reduction_state(const bool enabled) + { + strength_reduction_enabled_ = enabled; + } + + inline bool strength_reduction_enabled() const + { + return strength_reduction_enabled_; + } + + inline bool valid_operator(const details::operator_type& operation, binary_functor_t& bop) + { + typename binary_op_map_t::iterator bop_itr = binary_op_map_->find(operation); + + if (binary_op_map_->end() == bop_itr) + return false; + + bop = bop_itr->second; + + return true; + } + + inline bool valid_operator(const details::operator_type& operation, unary_functor_t& uop) + { + typename unary_op_map_t::iterator uop_itr = unary_op_map_->find(operation); + + if ((*unary_op_map_).end() == uop_itr) + return false; + + uop = uop_itr->second; + + return true; + } + + inline details::operator_type get_operator(const binary_functor_t& bop) const + { + return (*inv_binary_op_map_).find(bop)->second; + } + + inline expression_node_ptr operator() (const Type& v) const + { + return node_allocator_->allocate(v); + } + + #ifndef exprtk_disable_string_capabilities + inline expression_node_ptr operator() (const std::string& s) const + { + return node_allocator_->allocate(s); + } + + inline expression_node_ptr operator() (std::string& s, range_t& rp) const + { + return node_allocator_->allocate_rr(s,rp); + } + + inline expression_node_ptr operator() (const std::string& s, range_t& rp) const + { + return node_allocator_->allocate_tt(s,rp); + } + + inline expression_node_ptr operator() (expression_node_ptr branch, range_t& rp) const + { + if (is_generally_string_node(branch)) + return node_allocator_->allocate_tt(branch,rp); + else + return error_node(); + } + #endif + + inline bool unary_optimisable(const details::operator_type& operation) const + { + return (details::e_abs == operation) || (details::e_acos == operation) || + (details::e_acosh == operation) || (details::e_asin == operation) || + (details::e_asinh == operation) || (details::e_atan == operation) || + (details::e_atanh == operation) || (details::e_ceil == operation) || + (details::e_cos == operation) || (details::e_cosh == operation) || + (details::e_exp == operation) || (details::e_expm1 == operation) || + (details::e_floor == operation) || (details::e_log == operation) || + (details::e_log10 == operation) || (details::e_log2 == operation) || + (details::e_log1p == operation) || (details::e_neg == operation) || + (details::e_pos == operation) || (details::e_round == operation) || + (details::e_sin == operation) || (details::e_sinc == operation) || + (details::e_sinh == operation) || (details::e_sqrt == operation) || + (details::e_tan == operation) || (details::e_tanh == operation) || + (details::e_cot == operation) || (details::e_sec == operation) || + (details::e_csc == operation) || (details::e_r2d == operation) || + (details::e_d2r == operation) || (details::e_d2g == operation) || + (details::e_g2d == operation) || (details::e_notl == operation) || + (details::e_sgn == operation) || (details::e_erf == operation) || + (details::e_erfc == operation) || (details::e_ncdf == operation) || + (details::e_frac == operation) || (details::e_trunc == operation) ; + } + + inline bool sf3_optimisable(const std::string& sf3id, trinary_functor_t& tfunc) const + { + typename sf3_map_t::const_iterator itr = sf3_map_->find(sf3id); + + if (sf3_map_->end() == itr) + return false; + else + tfunc = itr->second.first; + + return true; + } + + inline bool sf4_optimisable(const std::string& sf4id, quaternary_functor_t& qfunc) const + { + typename sf4_map_t::const_iterator itr = sf4_map_->find(sf4id); + + if (sf4_map_->end() == itr) + return false; + else + qfunc = itr->second.first; + + return true; + } + + inline bool sf3_optimisable(const std::string& sf3id, details::operator_type& operation) const + { + typename sf3_map_t::const_iterator itr = sf3_map_->find(sf3id); + + if (sf3_map_->end() == itr) + return false; + else + operation = itr->second.second; + + return true; + } + + inline bool sf4_optimisable(const std::string& sf4id, details::operator_type& operation) const + { + typename sf4_map_t::const_iterator itr = sf4_map_->find(sf4id); + + if (sf4_map_->end() == itr) + return false; + else + operation = itr->second.second; + + return true; + } + + inline expression_node_ptr operator() (const details::operator_type& operation, expression_node_ptr (&branch)[1]) + { + if (0 == branch[0]) + { + return error_node(); + } + else if (details::is_null_node(branch[0])) + { + return branch[0]; + } + else if (details::is_break_node(branch[0])) + { + return error_node(); + } + else if (details::is_continue_node(branch[0])) + { + return error_node(); + } + else if (details::is_constant_node(branch[0])) + { + return synthesize_expression(operation,branch); + } + else if (unary_optimisable(operation) && details::is_variable_node(branch[0])) + { + return synthesize_uv_expression(operation,branch); + } + else if (unary_optimisable(operation) && details::is_ivector_node(branch[0])) + { + return synthesize_uvec_expression(operation,branch); + } + else + return synthesize_unary_expression(operation,branch); + } + + inline bool is_assignment_operation(const details::operator_type& operation) const + { + return ( + (details::e_addass == operation) || + (details::e_subass == operation) || + (details::e_mulass == operation) || + (details::e_divass == operation) || + (details::e_modass == operation) + ) && + parser_->settings_.assignment_enabled(operation); + } + + #ifndef exprtk_disable_string_capabilities + inline bool valid_string_operation(const details::operator_type& operation) const + { + return (details::e_add == operation) || + (details::e_lt == operation) || + (details::e_lte == operation) || + (details::e_gt == operation) || + (details::e_gte == operation) || + (details::e_eq == operation) || + (details::e_ne == operation) || + (details::e_in == operation) || + (details::e_like == operation) || + (details::e_ilike == operation) || + (details::e_assign == operation) || + (details::e_addass == operation) || + (details::e_swap == operation) ; + } + #else + inline bool valid_string_operation(const details::operator_type&) const + { + return false; + } + #endif + + inline std::string to_str(const details::operator_type& operation) const + { + switch (operation) + { + case details::e_add : return "+" ; + case details::e_sub : return "-" ; + case details::e_mul : return "*" ; + case details::e_div : return "/" ; + case details::e_mod : return "%" ; + case details::e_pow : return "^" ; + case details::e_lt : return "<" ; + case details::e_lte : return "<=" ; + case details::e_gt : return ">" ; + case details::e_gte : return ">=" ; + case details::e_eq : return "==" ; + case details::e_ne : return "!=" ; + case details::e_and : return "and" ; + case details::e_nand : return "nand" ; + case details::e_or : return "or" ; + case details::e_nor : return "nor" ; + case details::e_xor : return "xor" ; + case details::e_xnor : return "xnor" ; + default : return "UNKNOWN"; + } + } + + inline bool operation_optimisable(const details::operator_type& operation) const + { + return (details::e_add == operation) || + (details::e_sub == operation) || + (details::e_mul == operation) || + (details::e_div == operation) || + (details::e_mod == operation) || + (details::e_pow == operation) || + (details::e_lt == operation) || + (details::e_lte == operation) || + (details::e_gt == operation) || + (details::e_gte == operation) || + (details::e_eq == operation) || + (details::e_ne == operation) || + (details::e_and == operation) || + (details::e_nand == operation) || + (details::e_or == operation) || + (details::e_nor == operation) || + (details::e_xor == operation) || + (details::e_xnor == operation) ; + } + + inline std::string branch_to_id(expression_node_ptr branch) const + { + static const std::string null_str ("(null)" ); + static const std::string const_str ("(c)" ); + static const std::string var_str ("(v)" ); + static const std::string vov_str ("(vov)" ); + static const std::string cov_str ("(cov)" ); + static const std::string voc_str ("(voc)" ); + static const std::string str_str ("(s)" ); + static const std::string strrng_str ("(rngs)" ); + static const std::string cs_str ("(cs)" ); + static const std::string cstrrng_str("(crngs)"); + + if (details::is_null_node(branch)) + return null_str; + else if (details::is_constant_node(branch)) + return const_str; + else if (details::is_variable_node(branch)) + return var_str; + else if (details::is_vov_node(branch)) + return vov_str; + else if (details::is_cov_node(branch)) + return cov_str; + else if (details::is_voc_node(branch)) + return voc_str; + else if (details::is_string_node(branch)) + return str_str; + else if (details::is_const_string_node(branch)) + return cs_str; + else if (details::is_string_range_node(branch)) + return strrng_str; + else if (details::is_const_string_range_node(branch)) + return cstrrng_str; + else if (details::is_t0ot1ot2_node(branch)) + return "(" + dynamic_cast*>(branch)->type_id() + ")"; + else if (details::is_t0ot1ot2ot3_node(branch)) + return "(" + dynamic_cast*>(branch)->type_id() + ")"; + else + return "ERROR"; + } + + inline std::string branch_to_id(expression_node_ptr (&branch)[2]) const + { + return branch_to_id(branch[0]) + std::string("o") + branch_to_id(branch[1]); + } + + inline bool cov_optimisable(const details::operator_type& operation, expression_node_ptr (&branch)[2]) const + { + if (!operation_optimisable(operation)) + return false; + else + return details::is_constant_node(branch[0]) && + details::is_variable_node(branch[1]) ; + } + + inline bool voc_optimisable(const details::operator_type& operation, expression_node_ptr (&branch)[2]) const + { + if (!operation_optimisable(operation)) + return false; + else + return details::is_variable_node(branch[0]) && + details::is_constant_node(branch[1]) ; + } + + inline bool vov_optimisable(const details::operator_type& operation, expression_node_ptr (&branch)[2]) const + { + if (!operation_optimisable(operation)) + return false; + else + return details::is_variable_node(branch[0]) && + details::is_variable_node(branch[1]) ; + } + + inline bool cob_optimisable(const details::operator_type& operation, expression_node_ptr (&branch)[2]) const + { + if (!operation_optimisable(operation)) + return false; + else + return details::is_constant_node(branch[0]) && + !details::is_constant_node(branch[1]) ; + } + + inline bool boc_optimisable(const details::operator_type& operation, expression_node_ptr (&branch)[2]) const + { + if (!operation_optimisable(operation)) + return false; + else + return !details::is_constant_node(branch[0]) && + details::is_constant_node(branch[1]) ; + } + + inline bool cocob_optimisable(const details::operator_type& operation, expression_node_ptr (&branch)[2]) const + { + if ( + (details::e_add == operation) || + (details::e_sub == operation) || + (details::e_mul == operation) || + (details::e_div == operation) + ) + { + return (details::is_constant_node(branch[0]) && details::is_cob_node(branch[1])) || + (details::is_constant_node(branch[1]) && details::is_cob_node(branch[0])) ; + } + else + return false; + } + + inline bool coboc_optimisable(const details::operator_type& operation, expression_node_ptr (&branch)[2]) const + { + if ( + (details::e_add == operation) || + (details::e_sub == operation) || + (details::e_mul == operation) || + (details::e_div == operation) + ) + { + return (details::is_constant_node(branch[0]) && details::is_boc_node(branch[1])) || + (details::is_constant_node(branch[1]) && details::is_boc_node(branch[0])) ; + } + else + return false; + } + + inline bool uvouv_optimisable(const details::operator_type& operation, expression_node_ptr (&branch)[2]) const + { + if (!operation_optimisable(operation)) + return false; + else + return details::is_uv_node(branch[0]) && + details::is_uv_node(branch[1]) ; + } + + inline bool vob_optimisable(const details::operator_type& operation, expression_node_ptr (&branch)[2]) const + { + if (!operation_optimisable(operation)) + return false; + else + return details::is_variable_node(branch[0]) && + !details::is_variable_node(branch[1]) ; + } + + inline bool bov_optimisable(const details::operator_type& operation, expression_node_ptr (&branch)[2]) const + { + if (!operation_optimisable(operation)) + return false; + else + return !details::is_variable_node(branch[0]) && + details::is_variable_node(branch[1]) ; + } + + inline bool binext_optimisable(const details::operator_type& operation, expression_node_ptr (&branch)[2]) const + { + if (!operation_optimisable(operation)) + return false; + else + return !details::is_constant_node(branch[0]) || + !details::is_constant_node(branch[1]) ; + } + + inline bool is_invalid_assignment_op(const details::operator_type& operation, expression_node_ptr (&branch)[2]) const + { + if (is_assignment_operation(operation)) + { + const bool b1_is_genstring = details::is_generally_string_node(branch[1]); + + if (details::is_string_node(branch[0])) + return !b1_is_genstring; + else if (details::is_literal_node(branch[0])) + return true; + else + return ( + !details::is_variable_node (branch[0]) && + !details::is_vector_elem_node (branch[0]) && + !details::is_vector_celem_node (branch[0]) && + !details::is_vector_elem_rtc_node (branch[0]) && + !details::is_vector_celem_rtc_node (branch[0]) && + !details::is_rebasevector_elem_node (branch[0]) && + !details::is_rebasevector_celem_node (branch[0]) && + !details::is_rebasevector_elem_rtc_node (branch[0]) && + !details::is_rebasevector_celem_rtc_node(branch[0]) && + !details::is_vector_node (branch[0]) + ) + || b1_is_genstring; + } + else + return false; + } + + inline bool is_constpow_operation(const details::operator_type& operation, expression_node_ptr(&branch)[2]) const + { + if ( + !details::is_constant_node(branch[1]) || + details::is_constant_node(branch[0]) || + details::is_variable_node(branch[0]) || + details::is_vector_node (branch[0]) || + details::is_generally_string_node(branch[0]) + ) + return false; + + const Type c = static_cast*>(branch[1])->value(); + + return cardinal_pow_optimisable(operation, c); + } + + inline bool is_invalid_break_continue_op(expression_node_ptr (&branch)[2]) const + { + return ( + details::is_break_node (branch[0]) || + details::is_break_node (branch[1]) || + details::is_continue_node(branch[0]) || + details::is_continue_node(branch[1]) + ); + } + + inline bool is_invalid_string_op(const details::operator_type& operation, expression_node_ptr (&branch)[2]) const + { + const bool b0_string = is_generally_string_node(branch[0]); + const bool b1_string = is_generally_string_node(branch[1]); + + bool result = false; + + if (b0_string != b1_string) + result = true; + else if (!valid_string_operation(operation) && b0_string && b1_string) + result = true; + + if (result) + { + parser_->set_synthesis_error("Invalid string operation"); + } + + return result; + } + + inline bool is_invalid_string_op(const details::operator_type& operation, expression_node_ptr (&branch)[3]) const + { + const bool b0_string = is_generally_string_node(branch[0]); + const bool b1_string = is_generally_string_node(branch[1]); + const bool b2_string = is_generally_string_node(branch[2]); + + bool result = false; + + if ((b0_string != b1_string) || (b1_string != b2_string)) + result = true; + else if ((details::e_inrange != operation) && b0_string && b1_string && b2_string) + result = true; + + if (result) + { + parser_->set_synthesis_error("Invalid string operation"); + } + + return result; + } + + inline bool is_string_operation(const details::operator_type& operation, expression_node_ptr (&branch)[2]) const + { + const bool b0_string = is_generally_string_node(branch[0]); + const bool b1_string = is_generally_string_node(branch[1]); + + return (b0_string && b1_string && valid_string_operation(operation)); + } + + inline bool is_string_operation(const details::operator_type& operation, expression_node_ptr (&branch)[3]) const + { + const bool b0_string = is_generally_string_node(branch[0]); + const bool b1_string = is_generally_string_node(branch[1]); + const bool b2_string = is_generally_string_node(branch[2]); + + return (b0_string && b1_string && b2_string && (details::e_inrange == operation)); + } + + #ifndef exprtk_disable_sc_andor + inline bool is_shortcircuit_expression(const details::operator_type& operation) const + { + return ( + (details::e_scand == operation) || + (details::e_scor == operation) + ); + } + #else + inline bool is_shortcircuit_expression(const details::operator_type&) const + { + return false; + } + #endif + + inline bool is_null_present(expression_node_ptr (&branch)[2]) const + { + return ( + details::is_null_node(branch[0]) || + details::is_null_node(branch[1]) + ); + } + + inline bool is_vector_eqineq_logic_operation(const details::operator_type& operation, expression_node_ptr (&branch)[2]) const + { + if (!is_ivector_node(branch[0]) && !is_ivector_node(branch[1])) + return false; + else + return ( + (details::e_lt == operation) || + (details::e_lte == operation) || + (details::e_gt == operation) || + (details::e_gte == operation) || + (details::e_eq == operation) || + (details::e_ne == operation) || + (details::e_equal == operation) || + (details::e_and == operation) || + (details::e_nand == operation) || + (details::e_or == operation) || + (details::e_nor == operation) || + (details::e_xor == operation) || + (details::e_xnor == operation) + ); + } + + inline bool is_vector_arithmetic_operation(const details::operator_type& operation, expression_node_ptr (&branch)[2]) const + { + if (!is_ivector_node(branch[0]) && !is_ivector_node(branch[1])) + return false; + else + return ( + (details::e_add == operation) || + (details::e_sub == operation) || + (details::e_mul == operation) || + (details::e_div == operation) || + (details::e_pow == operation) + ); + } + + inline expression_node_ptr operator() (const details::operator_type& operation, expression_node_ptr (&branch)[2]) + { + if ((0 == branch[0]) || (0 == branch[1])) + { + parser_->set_error(parser_error::make_error( + parser_error::e_syntax, + parser_->current_state().token, + "ERR249 - Invalid branches received for operator '" + details::to_str(operation) + "'", + exprtk_error_location)); + + return error_node(); + } + else if (is_invalid_string_op(operation,branch)) + { + parser_->set_error(parser_error::make_error( + parser_error::e_syntax, + parser_->current_state().token, + "ERR250 - Invalid branch pair for string operator '" + details::to_str(operation) + "'", + exprtk_error_location)); + + return error_node(); + } + else if (is_invalid_assignment_op(operation,branch)) + { + parser_->set_error(parser_error::make_error( + parser_error::e_syntax, + parser_->current_state().token, + "ERR251 - Invalid branch pair for assignment operator '" + details::to_str(operation) + "'", + exprtk_error_location)); + + return error_node(); + } + else if (is_invalid_break_continue_op(branch)) + { + parser_->set_error(parser_error::make_error( + parser_error::e_syntax, + parser_->current_state().token, + "ERR252 - Invalid branch pair for break/continue operator '" + details::to_str(operation) + "'", + exprtk_error_location)); + + return error_node(); + } + else if (details::e_assign == operation) + { + return synthesize_assignment_expression(operation, branch); + } + else if (details::e_swap == operation) + { + return synthesize_swap_expression(branch); + } + else if (is_assignment_operation(operation)) + { + return synthesize_assignment_operation_expression(operation, branch); + } + else if (is_vector_eqineq_logic_operation(operation, branch)) + { + return synthesize_veceqineqlogic_operation_expression(operation, branch); + } + else if (is_vector_arithmetic_operation(operation, branch)) + { + return synthesize_vecarithmetic_operation_expression(operation, branch); + } + else if (is_shortcircuit_expression(operation)) + { + return synthesize_shortcircuit_expression(operation, branch); + } + else if (is_string_operation(operation, branch)) + { + return synthesize_string_expression(operation, branch); + } + else if (is_null_present(branch)) + { + return synthesize_null_expression(operation, branch); + } + #ifndef exprtk_disable_cardinal_pow_optimisation + else if (is_constpow_operation(operation, branch)) + { + return cardinal_pow_optimisation(branch); + } + #endif + + expression_node_ptr result = error_node(); + + #ifndef exprtk_disable_enhanced_features + if (synthesize_expression(operation, branch, result)) + { + return result; + } + else + #endif + + { + /* + Possible reductions: + 1. c o cob -> cob + 2. cob o c -> cob + 3. c o boc -> boc + 4. boc o c -> boc + */ + result = error_node(); + + if (cocob_optimisable(operation, branch)) + { + result = synthesize_cocob_expression::process((*this), operation, branch); + } + else if (coboc_optimisable(operation, branch) && (0 == result)) + { + result = synthesize_coboc_expression::process((*this), operation, branch); + } + + if (result) + return result; + } + + if (uvouv_optimisable(operation, branch)) + { + return synthesize_uvouv_expression(operation, branch); + } + else if (vob_optimisable(operation, branch)) + { + return synthesize_vob_expression::process((*this), operation, branch); + } + else if (bov_optimisable(operation, branch)) + { + return synthesize_bov_expression::process((*this), operation, branch); + } + else if (cob_optimisable(operation, branch)) + { + return synthesize_cob_expression::process((*this), operation, branch); + } + else if (boc_optimisable(operation, branch)) + { + return synthesize_boc_expression::process((*this), operation, branch); + } + #ifndef exprtk_disable_enhanced_features + else if (cov_optimisable(operation, branch)) + { + return synthesize_cov_expression::process((*this), operation, branch); + } + #endif + else if (binext_optimisable(operation, branch)) + { + return synthesize_binary_ext_expression::process((*this), operation, branch); + } + else + return synthesize_expression(operation, branch); + } + + inline expression_node_ptr operator() (const details::operator_type& operation, expression_node_ptr (&branch)[3]) + { + if ( + (0 == branch[0]) || + (0 == branch[1]) || + (0 == branch[2]) + ) + { + details::free_all_nodes(*node_allocator_,branch); + + parser_->set_error(parser_error::make_error( + parser_error::e_syntax, + parser_->current_state().token, + "ERR253 - Invalid branches operator '" + details::to_str(operation) + "'", + exprtk_error_location)); + + return error_node(); + } + else if (is_invalid_string_op(operation, branch)) + { + parser_->set_error(parser_error::make_error( + parser_error::e_syntax, + parser_->current_state().token, + "ERR254 - Invalid branches for string operator '" + details::to_str(operation) + "'", + exprtk_error_location)); + + return error_node(); + } + else if (is_string_operation(operation, branch)) + { + return synthesize_string_expression(operation, branch); + } + else + return synthesize_expression(operation, branch); + } + + inline expression_node_ptr operator() (const details::operator_type& operation, expression_node_ptr (&branch)[4]) + { + return synthesize_expression(operation,branch); + } + + inline expression_node_ptr operator() (const details::operator_type& operation, expression_node_ptr b0) + { + expression_node_ptr branch[1] = { b0 }; + return (*this)(operation,branch); + } + + inline expression_node_ptr operator() (const details::operator_type& operation, expression_node_ptr& b0, expression_node_ptr& b1) + { + expression_node_ptr result = error_node(); + + if ((0 != b0) && (0 != b1)) + { + expression_node_ptr branch[2] = { b0, b1 }; + result = expression_generator::operator()(operation, branch); + b0 = branch[0]; + b1 = branch[1]; + } + + return result; + } + + inline expression_node_ptr conditional(expression_node_ptr condition, + expression_node_ptr consequent, + expression_node_ptr alternative) const + { + if ((0 == condition) || (0 == consequent)) + { + details::free_node(*node_allocator_, condition ); + details::free_node(*node_allocator_, consequent ); + details::free_node(*node_allocator_, alternative); + + const std::string invalid_branches = + ((0 == condition ) ? std::string("condition ") : "") + + ((0 == consequent) ? std::string("consequent") : "") ; + + parser_->set_error(parser_error::make_error( + parser_error::e_parser, + parser_->current_state().token, + "ERR255 - Invalid " + invalid_branches + " for conditional statement", + exprtk_error_location)); + + return error_node(); + } + // Can the condition be immediately evaluated? if so optimise. + else if (details::is_constant_node(condition)) + { + // True branch + if (details::is_true(condition)) + { + details::free_node(*node_allocator_, condition ); + details::free_node(*node_allocator_, alternative); + + return consequent; + } + // False branch + else + { + details::free_node(*node_allocator_, condition ); + details::free_node(*node_allocator_, consequent); + + if (alternative) + return alternative; + else + return node_allocator_->allocate >(); + } + } + + expression_node_ptr result = error_node(); + std::string node_name = "Unknown!"; + + if ((0 != consequent) && (0 != alternative)) + { + result = node_allocator_->allocate(condition, consequent, alternative); + node_name = "conditional_node_t"; + } + else + { + result = node_allocator_->allocate(condition, consequent); + node_name = "cons_conditional_node_t"; + } + + if (result && result->valid()) + { + return result; + } + + parser_->set_error(parser_error::make_error( + parser_error::e_parser, + token_t(), + "ERR256 - Failed to synthesize node: " + node_name, + exprtk_error_location)); + + details::free_node(*node_allocator_, result); + return error_node(); + } + + #ifndef exprtk_disable_string_capabilities + inline expression_node_ptr conditional_string(expression_node_ptr condition, + expression_node_ptr consequent, + expression_node_ptr alternative) const + { + if ((0 == condition) || (0 == consequent)) + { + details::free_node(*node_allocator_, condition ); + details::free_node(*node_allocator_, consequent ); + details::free_node(*node_allocator_, alternative); + + const std::string invalid_branches = + ((0 == condition ) ? std::string("condition ") : "") + + ((0 == consequent) ? std::string("consequent") : "") ; + + parser_->set_error(parser_error::make_error( + parser_error::e_parser, + parser_->current_state().token, + "ERR257 - Invalid " + invalid_branches + " for string conditional statement", + exprtk_error_location)); + + return error_node(); + } + // Can the condition be immediately evaluated? if so optimise. + else if (details::is_constant_node(condition)) + { + // True branch + if (details::is_true(condition)) + { + details::free_node(*node_allocator_, condition ); + details::free_node(*node_allocator_, alternative); + + return consequent; + } + // False branch + else + { + details::free_node(*node_allocator_, condition ); + details::free_node(*node_allocator_, consequent); + + if (alternative) + return alternative; + else + return node_allocator_-> + allocate_c >(""); + } + } + else if ((0 != consequent) && (0 != alternative)) + { + expression_node_ptr result = + node_allocator_->allocate(condition, consequent, alternative); + + if (result && result->valid()) + { + return result; + } + + parser_->set_error(parser_error::make_error( + parser_error::e_parser, + token_t(), + "ERR258 - Failed to synthesize node: conditional_string_node_t", + exprtk_error_location)); + + details::free_node(*node_allocator_, result); + } + + return error_node(); + } + #else + inline expression_node_ptr conditional_string(expression_node_ptr, + expression_node_ptr, + expression_node_ptr) const + { + return error_node(); + } + #endif + + inline expression_node_ptr conditional_vector(expression_node_ptr condition, + expression_node_ptr consequent, + expression_node_ptr alternative) const + { + if ((0 == condition) || (0 == consequent)) + { + details::free_node(*node_allocator_, condition ); + details::free_node(*node_allocator_, consequent ); + details::free_node(*node_allocator_, alternative); + + const std::string invalid_branches = + ((0 == condition ) ? std::string("condition ") : "") + + ((0 == consequent) ? std::string("consequent") : "") ; + + parser_->set_error(parser_error::make_error( + parser_error::e_parser, + parser_->current_state().token, + "ERR259 - Invalid " + invalid_branches + " for vector conditional statement", + exprtk_error_location)); + + return error_node(); + } + // Can the condition be immediately evaluated? if so optimise. + else if (details::is_constant_node(condition)) + { + // True branch + if (details::is_true(condition)) + { + details::free_node(*node_allocator_, condition ); + details::free_node(*node_allocator_, alternative); + + return consequent; + } + // False branch + else + { + details::free_node(*node_allocator_, condition ); + details::free_node(*node_allocator_, consequent); + + if (alternative) + return alternative; + else + return node_allocator_->allocate >(); + + } + } + else if ((0 != consequent) && (0 != alternative)) + { + return node_allocator_-> + allocate(condition, consequent, alternative); + } + else + return error_node(); + } + + inline loop_runtime_check_ptr get_loop_runtime_check(const loop_runtime_check::loop_types loop_type) const + { + if ( + parser_->loop_runtime_check_ && + (loop_type == (parser_->loop_runtime_check_->loop_set & loop_type)) + ) + { + return parser_->loop_runtime_check_; + } + + return loop_runtime_check_ptr(0); + } + + inline vector_access_runtime_check_ptr get_vector_access_runtime_check() const + { + return parser_->vector_access_runtime_check_; + } + + inline expression_node_ptr while_loop(expression_node_ptr& condition, + expression_node_ptr& branch, + const bool break_continue_present = false) const + { + if ( + !break_continue_present && + !parser_->state_.return_stmt_present && + details::is_constant_node(condition) + ) + { + expression_node_ptr result = error_node(); + if (details::is_true(condition)) + { + // Infinite loops are not allowed. + + parser_->set_error(parser_error::make_error( + parser_error::e_parser, + parser_->current_state().token, + "ERR260 - Infinite loop condition without 'break' or 'return' not allowed in while-loops", + exprtk_error_location)); + + result = error_node(); + } + else + result = node_allocator_->allocate >(); + + details::free_node(*node_allocator_, condition); + details::free_node(*node_allocator_, branch ); + + return result; + } + else if (details::is_null_node(condition)) + { + details::free_node(*node_allocator_,condition); + + return branch; + } + + loop_runtime_check_ptr rtc = get_loop_runtime_check(loop_runtime_check::e_while_loop); + + if (!break_continue_present) + { + if (rtc) + return node_allocator_->allocate + (condition, branch, rtc); + else + return node_allocator_->allocate + (condition, branch); + } + #ifndef exprtk_disable_break_continue + else + { + if (rtc) + return node_allocator_->allocate + (condition, branch, rtc); + else + return node_allocator_->allocate + (condition, branch); + } + #else + return error_node(); + #endif + } + + inline expression_node_ptr repeat_until_loop(expression_node_ptr& condition, + expression_node_ptr& branch, + const bool break_continue_present = false) const + { + if (!break_continue_present && details::is_constant_node(condition)) + { + if ( + details::is_true(condition) && + details::is_constant_node(branch) + ) + { + free_node(*node_allocator_,condition); + + return branch; + } + + details::free_node(*node_allocator_, condition); + details::free_node(*node_allocator_, branch ); + + return error_node(); + } + else if (details::is_null_node(condition)) + { + details::free_node(*node_allocator_,condition); + + return branch; + } + + loop_runtime_check_ptr rtc = get_loop_runtime_check(loop_runtime_check::e_repeat_until_loop); + + if (!break_continue_present) + { + if (rtc) + return node_allocator_->allocate + (condition, branch, rtc); + else + return node_allocator_->allocate + (condition, branch); + } + #ifndef exprtk_disable_break_continue + else + { + if (rtc) + return node_allocator_->allocate + (condition, branch, rtc); + else + return node_allocator_->allocate + (condition, branch); + } + #else + return error_node(); + #endif + } + + inline expression_node_ptr for_loop(expression_node_ptr& initialiser, + expression_node_ptr& condition, + expression_node_ptr& incrementor, + expression_node_ptr& loop_body, + bool break_continue_present = false) const + { + if ( + !break_continue_present && + !parser_->state_.return_stmt_present && + details::is_constant_node(condition) + ) + { + expression_node_ptr result = error_node(); + + if (details::is_true(condition)) + { + // Infinite loops are not allowed. + + parser_->set_error(parser_error::make_error( + parser_error::e_parser, + parser_->current_state().token, + "ERR261 - Infinite loop condition without 'break' or 'return' not allowed in for-loop", + exprtk_error_location)); + + result = error_node(); + } + else + result = node_allocator_->allocate >(); + + details::free_node(*node_allocator_, initialiser); + details::free_node(*node_allocator_, condition ); + details::free_node(*node_allocator_, incrementor); + details::free_node(*node_allocator_, loop_body ); + + return result; + } + else if (details::is_null_node(condition) || (0 == condition)) + { + details::free_node(*node_allocator_, initialiser); + details::free_node(*node_allocator_, condition ); + details::free_node(*node_allocator_, incrementor); + + return loop_body; + } + + loop_runtime_check_ptr rtc = get_loop_runtime_check(loop_runtime_check::e_for_loop); + + if (!break_continue_present) + { + if (rtc) + return node_allocator_->allocate + ( + initialiser, + condition, + incrementor, + loop_body, + rtc + ); + else + return node_allocator_->allocate + ( + initialiser, + condition, + incrementor, + loop_body + ); + } + #ifndef exprtk_disable_break_continue + else + { + if (rtc) + return node_allocator_->allocate + ( + initialiser, + condition, + incrementor, + loop_body, + rtc + ); + else + return node_allocator_->allocate + ( + initialiser, + condition, + incrementor, + loop_body + ); + } + #else + return error_node(); + #endif + } + + template class Sequence> + inline expression_node_ptr const_optimise_switch(Sequence& arg_list) + { + expression_node_ptr result = error_node(); + + for (std::size_t i = 0; i < (arg_list.size() / 2); ++i) + { + expression_node_ptr condition = arg_list[(2 * i) ]; + expression_node_ptr consequent = arg_list[(2 * i) + 1]; + + if ((0 == result) && details::is_true(condition)) + { + result = consequent; + break; + } + } + + if (0 == result) + { + result = arg_list.back(); + } + + for (std::size_t i = 0; i < arg_list.size(); ++i) + { + expression_node_ptr current_expr = arg_list[i]; + + if (current_expr && (current_expr != result)) + { + free_node(*node_allocator_,current_expr); + } + } + + return result; + } + + template class Sequence> + inline expression_node_ptr const_optimise_mswitch(Sequence& arg_list) + { + expression_node_ptr result = error_node(); + + for (std::size_t i = 0; i < (arg_list.size() / 2); ++i) + { + expression_node_ptr condition = arg_list[(2 * i) ]; + expression_node_ptr consequent = arg_list[(2 * i) + 1]; + + if (details::is_true(condition)) + { + result = consequent; + } + } + + if (0 == result) + { + const T zero = T(0); + result = node_allocator_->allocate(zero); + } + + for (std::size_t i = 0; i < arg_list.size(); ++i) + { + expression_node_ptr& current_expr = arg_list[i]; + + if (current_expr && (current_expr != result)) + { + details::free_node(*node_allocator_,current_expr); + } + } + + return result; + } + + struct switch_nodes + { + typedef std::vector > arg_list_t; + + #define case_stmt(N) \ + if (is_true(arg[(2 * N)].first)) { return arg[(2 * N) + 1].first->value(); } \ + + struct switch_impl_1 + { + static inline T process(const arg_list_t& arg) + { + case_stmt(0) + + assert(arg.size() == ((2 * 1) + 1)); + + return arg.back().first->value(); + } + }; + + struct switch_impl_2 + { + static inline T process(const arg_list_t& arg) + { + case_stmt(0) case_stmt(1) + + assert(arg.size() == ((2 * 2) + 1)); + + return arg.back().first->value(); + } + }; + + struct switch_impl_3 + { + static inline T process(const arg_list_t& arg) + { + case_stmt(0) case_stmt(1) + case_stmt(2) + + assert(arg.size() == ((2 * 3) + 1)); + + return arg.back().first->value(); + } + }; + + struct switch_impl_4 + { + static inline T process(const arg_list_t& arg) + { + case_stmt(0) case_stmt(1) + case_stmt(2) case_stmt(3) + + assert(arg.size() == ((2 * 4) + 1)); + + return arg.back().first->value(); + } + }; + + struct switch_impl_5 + { + static inline T process(const arg_list_t& arg) + { + case_stmt(0) case_stmt(1) + case_stmt(2) case_stmt(3) + case_stmt(4) + + assert(arg.size() == ((2 * 5) + 1)); + + return arg.back().first->value(); + } + }; + + struct switch_impl_6 + { + static inline T process(const arg_list_t& arg) + { + case_stmt(0) case_stmt(1) + case_stmt(2) case_stmt(3) + case_stmt(4) case_stmt(5) + + assert(arg.size() == ((2 * 6) + 1)); + + return arg.back().first->value(); + } + }; + + struct switch_impl_7 + { + static inline T process(const arg_list_t& arg) + { + case_stmt(0) case_stmt(1) + case_stmt(2) case_stmt(3) + case_stmt(4) case_stmt(5) + case_stmt(6) + + assert(arg.size() == ((2 * 7) + 1)); + + return arg.back().first->value(); + } + }; + + #undef case_stmt + }; + + template class Sequence> + inline expression_node_ptr switch_statement(Sequence& arg_list, const bool default_statement_present) + { + if (arg_list.empty()) + return error_node(); + else if ( + !all_nodes_valid(arg_list) || + (!default_statement_present && (arg_list.size() < 2)) + ) + { + details::free_all_nodes(*node_allocator_,arg_list); + + return error_node(); + } + else if (is_constant_foldable(arg_list)) + return const_optimise_switch(arg_list); + + switch ((arg_list.size() - 1) / 2) + { + #define case_stmt(N) \ + case N : \ + return node_allocator_-> \ + allocate >(arg_list); \ + + case_stmt(1) + case_stmt(2) + case_stmt(3) + case_stmt(4) + case_stmt(5) + case_stmt(6) + case_stmt(7) + #undef case_stmt + + default : return node_allocator_->allocate >(arg_list); + } + } + + template class Sequence> + inline expression_node_ptr multi_switch_statement(Sequence& arg_list) + { + if (!all_nodes_valid(arg_list)) + { + details::free_all_nodes(*node_allocator_,arg_list); + + return error_node(); + } + else if (is_constant_foldable(arg_list)) + return const_optimise_mswitch(arg_list); + else + return node_allocator_->allocate >(arg_list); + } + + inline expression_node_ptr assert_call(expression_node_ptr& assert_condition, + expression_node_ptr& assert_message, + const assert_check::assert_context& context) + { + typedef details::assert_node alloc_type; + + expression_node_ptr result = node_allocator_->allocate_rrrr + (assert_condition, assert_message, parser_->assert_check_, context); + + if (result && result->valid()) + { + parser_->state_.activate_side_effect("assert_call()"); + return result; + } + + details::free_node(*node_allocator_, result ); + details::free_node(*node_allocator_, assert_condition); + details::free_node(*node_allocator_, assert_message ); + + return error_node(); + } + + #define unary_opr_switch_statements \ + case_stmt(details::e_abs , details::abs_op ) \ + case_stmt(details::e_acos , details::acos_op ) \ + case_stmt(details::e_acosh , details::acosh_op) \ + case_stmt(details::e_asin , details::asin_op ) \ + case_stmt(details::e_asinh , details::asinh_op) \ + case_stmt(details::e_atan , details::atan_op ) \ + case_stmt(details::e_atanh , details::atanh_op) \ + case_stmt(details::e_ceil , details::ceil_op ) \ + case_stmt(details::e_cos , details::cos_op ) \ + case_stmt(details::e_cosh , details::cosh_op ) \ + case_stmt(details::e_exp , details::exp_op ) \ + case_stmt(details::e_expm1 , details::expm1_op) \ + case_stmt(details::e_floor , details::floor_op) \ + case_stmt(details::e_log , details::log_op ) \ + case_stmt(details::e_log10 , details::log10_op) \ + case_stmt(details::e_log2 , details::log2_op ) \ + case_stmt(details::e_log1p , details::log1p_op) \ + case_stmt(details::e_neg , details::neg_op ) \ + case_stmt(details::e_pos , details::pos_op ) \ + case_stmt(details::e_round , details::round_op) \ + case_stmt(details::e_sin , details::sin_op ) \ + case_stmt(details::e_sinc , details::sinc_op ) \ + case_stmt(details::e_sinh , details::sinh_op ) \ + case_stmt(details::e_sqrt , details::sqrt_op ) \ + case_stmt(details::e_tan , details::tan_op ) \ + case_stmt(details::e_tanh , details::tanh_op ) \ + case_stmt(details::e_cot , details::cot_op ) \ + case_stmt(details::e_sec , details::sec_op ) \ + case_stmt(details::e_csc , details::csc_op ) \ + case_stmt(details::e_r2d , details::r2d_op ) \ + case_stmt(details::e_d2r , details::d2r_op ) \ + case_stmt(details::e_d2g , details::d2g_op ) \ + case_stmt(details::e_g2d , details::g2d_op ) \ + case_stmt(details::e_notl , details::notl_op ) \ + case_stmt(details::e_sgn , details::sgn_op ) \ + case_stmt(details::e_erf , details::erf_op ) \ + case_stmt(details::e_erfc , details::erfc_op ) \ + case_stmt(details::e_ncdf , details::ncdf_op ) \ + case_stmt(details::e_frac , details::frac_op ) \ + case_stmt(details::e_trunc , details::trunc_op) \ + + inline expression_node_ptr synthesize_uv_expression(const details::operator_type& operation, + expression_node_ptr (&branch)[1]) + { + T& v = static_cast*>(branch[0])->ref(); + + switch (operation) + { + #define case_stmt(op0, op1) \ + case op0 : return node_allocator_-> \ + allocate > >(v); \ + + unary_opr_switch_statements + #undef case_stmt + default : return error_node(); + } + } + + inline expression_node_ptr synthesize_uvec_expression(const details::operator_type& operation, + expression_node_ptr (&branch)[1]) + { + switch (operation) + { + #define case_stmt(op0, op1) \ + case op0 : return node_allocator_-> \ + allocate > > \ + (operation, branch[0]); \ + + unary_opr_switch_statements + #undef case_stmt + default : return error_node(); + } + } + + inline expression_node_ptr synthesize_unary_expression(const details::operator_type& operation, + expression_node_ptr (&branch)[1]) + { + switch (operation) + { + #define case_stmt(op0, op1) \ + case op0 : return node_allocator_-> \ + allocate > >(branch[0]); \ + + unary_opr_switch_statements + #undef case_stmt + default : return error_node(); + } + } + + inline expression_node_ptr const_optimise_sf3(const details::operator_type& operation, + expression_node_ptr (&branch)[3]) + { + expression_node_ptr temp_node = error_node(); + + switch (operation) + { + #define case_stmt(op) \ + case details::e_sf##op : temp_node = node_allocator_-> \ + allocate > > \ + (operation, branch); \ + break; \ + + case_stmt(00) case_stmt(01) case_stmt(02) case_stmt(03) + case_stmt(04) case_stmt(05) case_stmt(06) case_stmt(07) + case_stmt(08) case_stmt(09) case_stmt(10) case_stmt(11) + case_stmt(12) case_stmt(13) case_stmt(14) case_stmt(15) + case_stmt(16) case_stmt(17) case_stmt(18) case_stmt(19) + case_stmt(20) case_stmt(21) case_stmt(22) case_stmt(23) + case_stmt(24) case_stmt(25) case_stmt(26) case_stmt(27) + case_stmt(28) case_stmt(29) case_stmt(30) case_stmt(31) + case_stmt(32) case_stmt(33) case_stmt(34) case_stmt(35) + case_stmt(36) case_stmt(37) case_stmt(38) case_stmt(39) + case_stmt(40) case_stmt(41) case_stmt(42) case_stmt(43) + case_stmt(44) case_stmt(45) case_stmt(46) case_stmt(47) + #undef case_stmt + default : return error_node(); + } + + assert(temp_node); + + const T v = temp_node->value(); + + details::free_node(*node_allocator_,temp_node); + + return node_allocator_->allocate(v); + } + + inline expression_node_ptr varnode_optimise_sf3(const details::operator_type& operation, expression_node_ptr (&branch)[3]) + { + typedef details::variable_node* variable_ptr; + + const Type& v0 = static_cast(branch[0])->ref(); + const Type& v1 = static_cast(branch[1])->ref(); + const Type& v2 = static_cast(branch[2])->ref(); + + switch (operation) + { + #define case_stmt(op) \ + case details::e_sf##op : return node_allocator_-> \ + allocate_rrr > > \ + (v0, v1, v2); \ + + case_stmt(00) case_stmt(01) case_stmt(02) case_stmt(03) + case_stmt(04) case_stmt(05) case_stmt(06) case_stmt(07) + case_stmt(08) case_stmt(09) case_stmt(10) case_stmt(11) + case_stmt(12) case_stmt(13) case_stmt(14) case_stmt(15) + case_stmt(16) case_stmt(17) case_stmt(18) case_stmt(19) + case_stmt(20) case_stmt(21) case_stmt(22) case_stmt(23) + case_stmt(24) case_stmt(25) case_stmt(26) case_stmt(27) + case_stmt(28) case_stmt(29) case_stmt(30) case_stmt(31) + case_stmt(32) case_stmt(33) case_stmt(34) case_stmt(35) + case_stmt(36) case_stmt(37) case_stmt(38) case_stmt(39) + case_stmt(40) case_stmt(41) case_stmt(42) case_stmt(43) + case_stmt(44) case_stmt(45) case_stmt(46) case_stmt(47) + #undef case_stmt + default : return error_node(); + } + } + + inline expression_node_ptr special_function(const details::operator_type& operation, expression_node_ptr (&branch)[3]) + { + if (!all_nodes_valid(branch)) + return error_node(); + else if (is_constant_foldable(branch)) + return const_optimise_sf3(operation,branch); + else if (all_nodes_variables(branch)) + return varnode_optimise_sf3(operation,branch); + else + { + switch (operation) + { + #define case_stmt(op) \ + case details::e_sf##op : return node_allocator_-> \ + allocate > > \ + (operation, branch); \ + + case_stmt(00) case_stmt(01) case_stmt(02) case_stmt(03) + case_stmt(04) case_stmt(05) case_stmt(06) case_stmt(07) + case_stmt(08) case_stmt(09) case_stmt(10) case_stmt(11) + case_stmt(12) case_stmt(13) case_stmt(14) case_stmt(15) + case_stmt(16) case_stmt(17) case_stmt(18) case_stmt(19) + case_stmt(20) case_stmt(21) case_stmt(22) case_stmt(23) + case_stmt(24) case_stmt(25) case_stmt(26) case_stmt(27) + case_stmt(28) case_stmt(29) case_stmt(30) case_stmt(31) + case_stmt(32) case_stmt(33) case_stmt(34) case_stmt(35) + case_stmt(36) case_stmt(37) case_stmt(38) case_stmt(39) + case_stmt(40) case_stmt(41) case_stmt(42) case_stmt(43) + case_stmt(44) case_stmt(45) case_stmt(46) case_stmt(47) + #undef case_stmt + default : return error_node(); + } + } + } + + inline expression_node_ptr const_optimise_sf4(const details::operator_type& operation, expression_node_ptr (&branch)[4]) + { + expression_node_ptr temp_node = error_node(); + + switch (operation) + { + #define case_stmt(op) \ + case details::e_sf##op : temp_node = node_allocator_-> \ + allocate > > \ + (operation, branch); \ + break; \ + + case_stmt(48) case_stmt(49) case_stmt(50) case_stmt(51) + case_stmt(52) case_stmt(53) case_stmt(54) case_stmt(55) + case_stmt(56) case_stmt(57) case_stmt(58) case_stmt(59) + case_stmt(60) case_stmt(61) case_stmt(62) case_stmt(63) + case_stmt(64) case_stmt(65) case_stmt(66) case_stmt(67) + case_stmt(68) case_stmt(69) case_stmt(70) case_stmt(71) + case_stmt(72) case_stmt(73) case_stmt(74) case_stmt(75) + case_stmt(76) case_stmt(77) case_stmt(78) case_stmt(79) + case_stmt(80) case_stmt(81) case_stmt(82) case_stmt(83) + case_stmt(84) case_stmt(85) case_stmt(86) case_stmt(87) + case_stmt(88) case_stmt(89) case_stmt(90) case_stmt(91) + case_stmt(92) case_stmt(93) case_stmt(94) case_stmt(95) + case_stmt(96) case_stmt(97) case_stmt(98) case_stmt(99) + #undef case_stmt + default : return error_node(); + } + + assert(temp_node); + + const T v = temp_node->value(); + + details::free_node(*node_allocator_,temp_node); + + return node_allocator_->allocate(v); + } + + inline expression_node_ptr varnode_optimise_sf4(const details::operator_type& operation, expression_node_ptr (&branch)[4]) + { + typedef details::variable_node* variable_ptr; + + const Type& v0 = static_cast(branch[0])->ref(); + const Type& v1 = static_cast(branch[1])->ref(); + const Type& v2 = static_cast(branch[2])->ref(); + const Type& v3 = static_cast(branch[3])->ref(); + + switch (operation) + { + #define case_stmt(op) \ + case details::e_sf##op : return node_allocator_-> \ + allocate_rrrr > > \ + (v0, v1, v2, v3); \ + + case_stmt(48) case_stmt(49) case_stmt(50) case_stmt(51) + case_stmt(52) case_stmt(53) case_stmt(54) case_stmt(55) + case_stmt(56) case_stmt(57) case_stmt(58) case_stmt(59) + case_stmt(60) case_stmt(61) case_stmt(62) case_stmt(63) + case_stmt(64) case_stmt(65) case_stmt(66) case_stmt(67) + case_stmt(68) case_stmt(69) case_stmt(70) case_stmt(71) + case_stmt(72) case_stmt(73) case_stmt(74) case_stmt(75) + case_stmt(76) case_stmt(77) case_stmt(78) case_stmt(79) + case_stmt(80) case_stmt(81) case_stmt(82) case_stmt(83) + case_stmt(84) case_stmt(85) case_stmt(86) case_stmt(87) + case_stmt(88) case_stmt(89) case_stmt(90) case_stmt(91) + case_stmt(92) case_stmt(93) case_stmt(94) case_stmt(95) + case_stmt(96) case_stmt(97) case_stmt(98) case_stmt(99) + #undef case_stmt + default : return error_node(); + } + } + + inline expression_node_ptr special_function(const details::operator_type& operation, expression_node_ptr (&branch)[4]) + { + if (!all_nodes_valid(branch)) + return error_node(); + else if (is_constant_foldable(branch)) + return const_optimise_sf4(operation,branch); + else if (all_nodes_variables(branch)) + return varnode_optimise_sf4(operation,branch); + switch (operation) + { + #define case_stmt(op) \ + case details::e_sf##op : return node_allocator_-> \ + allocate > > \ + (operation, branch); \ + + case_stmt(48) case_stmt(49) case_stmt(50) case_stmt(51) + case_stmt(52) case_stmt(53) case_stmt(54) case_stmt(55) + case_stmt(56) case_stmt(57) case_stmt(58) case_stmt(59) + case_stmt(60) case_stmt(61) case_stmt(62) case_stmt(63) + case_stmt(64) case_stmt(65) case_stmt(66) case_stmt(67) + case_stmt(68) case_stmt(69) case_stmt(70) case_stmt(71) + case_stmt(72) case_stmt(73) case_stmt(74) case_stmt(75) + case_stmt(76) case_stmt(77) case_stmt(78) case_stmt(79) + case_stmt(80) case_stmt(81) case_stmt(82) case_stmt(83) + case_stmt(84) case_stmt(85) case_stmt(86) case_stmt(87) + case_stmt(88) case_stmt(89) case_stmt(90) case_stmt(91) + case_stmt(92) case_stmt(93) case_stmt(94) case_stmt(95) + case_stmt(96) case_stmt(97) case_stmt(98) case_stmt(99) + #undef case_stmt + default : return error_node(); + } + } + + template class Sequence> + inline expression_node_ptr const_optimise_varargfunc(const details::operator_type& operation, Sequence& arg_list) + { + expression_node_ptr temp_node = error_node(); + + switch (operation) + { + #define case_stmt(op0, op1) \ + case op0 : temp_node = node_allocator_-> \ + allocate > > \ + (arg_list); \ + break; \ + + case_stmt(details::e_sum , details::vararg_add_op ) + case_stmt(details::e_prod , details::vararg_mul_op ) + case_stmt(details::e_avg , details::vararg_avg_op ) + case_stmt(details::e_min , details::vararg_min_op ) + case_stmt(details::e_max , details::vararg_max_op ) + case_stmt(details::e_mand , details::vararg_mand_op ) + case_stmt(details::e_mor , details::vararg_mor_op ) + case_stmt(details::e_multi , details::vararg_multi_op) + #undef case_stmt + default : return error_node(); + } + + const T v = temp_node->value(); + + details::free_node(*node_allocator_,temp_node); + + return node_allocator_->allocate(v); + } + + inline bool special_one_parameter_vararg(const details::operator_type& operation) const + { + return ( + (details::e_sum == operation) || + (details::e_prod == operation) || + (details::e_avg == operation) || + (details::e_min == operation) || + (details::e_max == operation) + ); + } + + template class Sequence> + inline expression_node_ptr varnode_optimise_varargfunc(const details::operator_type& operation, + Sequence& arg_list) + { + switch (operation) + { + #define case_stmt(op0, op1) \ + case op0 : return node_allocator_-> \ + allocate > >(arg_list); \ + + case_stmt(details::e_sum , details::vararg_add_op ) + case_stmt(details::e_prod , details::vararg_mul_op ) + case_stmt(details::e_avg , details::vararg_avg_op ) + case_stmt(details::e_min , details::vararg_min_op ) + case_stmt(details::e_max , details::vararg_max_op ) + case_stmt(details::e_mand , details::vararg_mand_op ) + case_stmt(details::e_mor , details::vararg_mor_op ) + case_stmt(details::e_multi , details::vararg_multi_op) + #undef case_stmt + default : return error_node(); + } + } + + template class Sequence> + inline expression_node_ptr vectorize_func(const details::operator_type& operation, + Sequence& arg_list) + { + if (1 == arg_list.size()) + { + switch (operation) + { + #define case_stmt(op0, op1) \ + case op0 : return node_allocator_-> \ + allocate > >(arg_list[0]); \ + + case_stmt(details::e_sum , details::vec_add_op) + case_stmt(details::e_prod , details::vec_mul_op) + case_stmt(details::e_avg , details::vec_avg_op) + case_stmt(details::e_min , details::vec_min_op) + case_stmt(details::e_max , details::vec_max_op) + #undef case_stmt + default : return error_node(); + } + } + else + return error_node(); + } + + template class Sequence> + inline expression_node_ptr vararg_function(const details::operator_type& operation, + Sequence& arg_list) + { + if (!all_nodes_valid(arg_list)) + { + details::free_all_nodes(*node_allocator_,arg_list); + + return error_node(); + } + else if (is_constant_foldable(arg_list)) + return const_optimise_varargfunc(operation,arg_list); + else if ((1 == arg_list.size()) && details::is_ivector_node(arg_list[0])) + return vectorize_func(operation,arg_list); + else if ((1 == arg_list.size()) && special_one_parameter_vararg(operation)) + return arg_list[0]; + else if (all_nodes_variables(arg_list)) + return varnode_optimise_varargfunc(operation,arg_list); + + #ifndef exprtk_disable_string_capabilities + if (details::e_smulti == operation) + { + expression_node_ptr result = node_allocator_-> + allocate > >(arg_list); + if (result && result->valid()) + { + return result; + } + + parser_->set_error(parser_error::make_error( + parser_error::e_synthesis, + token_t(), + "ERR262 - Failed to synthesize node: str_vararg_node", + exprtk_error_location)); + + details::free_node(*node_allocator_, result); + } + else + #endif + { + expression_node_ptr result = error_node(); + + switch (operation) + { + #define case_stmt(op0, op1) \ + case op0 : result = node_allocator_-> \ + allocate > >(arg_list); \ + break; \ + + case_stmt(details::e_sum , details::vararg_add_op ) + case_stmt(details::e_prod , details::vararg_mul_op ) + case_stmt(details::e_avg , details::vararg_avg_op ) + case_stmt(details::e_min , details::vararg_min_op ) + case_stmt(details::e_max , details::vararg_max_op ) + case_stmt(details::e_mand , details::vararg_mand_op ) + case_stmt(details::e_mor , details::vararg_mor_op ) + case_stmt(details::e_multi , details::vararg_multi_op) + #undef case_stmt + default : return error_node(); + } + + if (result && result->valid()) + { + return result; + } + + parser_->set_error(parser_error::make_error( + parser_error::e_synthesis, + token_t(), + "ERR263 - Failed to synthesize node: vararg_node", + exprtk_error_location)); + + details::free_node(*node_allocator_, result); + } + + return error_node(); + } + + template + inline expression_node_ptr function(ifunction_t* f, expression_node_ptr (&b)[N]) + { + typedef typename details::function_N_node function_N_node_t; + expression_node_ptr result = synthesize_expression(f,b); + + if (0 == result) + return error_node(); + else + { + // Can the function call be completely optimised? + if (details::is_constant_node(result)) + return result; + else if (!all_nodes_valid(b)) + { + details::free_node(*node_allocator_,result); + std::fill_n(b, N, reinterpret_cast(0)); + + return error_node(); + } + else if (N != f->param_count) + { + details::free_node(*node_allocator_,result); + std::fill_n(b, N, reinterpret_cast(0)); + + return error_node(); + } + + function_N_node_t* func_node_ptr = reinterpret_cast(result); + + if (!func_node_ptr->init_branches(b)) + { + details::free_node(*node_allocator_,result); + std::fill_n(b, N, reinterpret_cast(0)); + + return error_node(); + } + + if (result && result->valid()) + { + return result; + } + + parser_->set_error(parser_error::make_error( + parser_error::e_synthesis, + token_t(), + "ERR264 - Failed to synthesize node: function_N_node_t", + exprtk_error_location)); + + details::free_node(*node_allocator_, result); + return error_node(); + } + } + + inline expression_node_ptr function(ifunction_t* f) + { + typedef typename details::function_N_node function_N_node_t; + return node_allocator_->allocate(f); + } + + inline expression_node_ptr vararg_function_call(ivararg_function_t* vaf, + std::vector& arg_list) + { + if (!all_nodes_valid(arg_list)) + { + details::free_all_nodes(*node_allocator_,arg_list); + + return error_node(); + } + + typedef details::vararg_function_node alloc_type; + + expression_node_ptr result = node_allocator_->allocate(vaf,arg_list); + + if ( + !arg_list.empty() && + !vaf->has_side_effects() && + is_constant_foldable(arg_list) + ) + { + const Type v = result->value(); + details::free_node(*node_allocator_,result); + result = node_allocator_->allocate(v); + } + + parser_->state_.activate_side_effect("vararg_function_call()"); + + if (result && result->valid()) + { + return result; + } + + parser_->set_error(parser_error::make_error( + parser_error::e_synthesis, + token_t(), + "ERR265 - Failed to synthesize node: vararg_function_node", + exprtk_error_location)); + + details::free_node(*node_allocator_, result); + return error_node(); + } + + inline expression_node_ptr generic_function_call(igeneric_function_t* gf, + std::vector& arg_list, + const std::size_t& param_seq_index = std::numeric_limits::max()) + { + if (!all_nodes_valid(arg_list)) + { + details::free_all_nodes(*node_allocator_,arg_list); + return error_node(); + } + + typedef details::generic_function_node alloc_type1; + typedef details::multimode_genfunction_node alloc_type2; + + const std::size_t no_psi = std::numeric_limits::max(); + + expression_node_ptr result = error_node(); + std::string node_name = "Unknown"; + + if (no_psi == param_seq_index) + { + result = node_allocator_->allocate(arg_list,gf); + node_name = "generic_function_node"; + } + else + { + result = node_allocator_->allocate(gf, param_seq_index, arg_list); + node_name = "multimode_genfunction_node"; + } + + alloc_type1* genfunc_node_ptr = static_cast(result); + + assert(genfunc_node_ptr); + + if ( + !arg_list.empty() && + !gf->has_side_effects() && + parser_->state_.type_check_enabled && + is_constant_foldable(arg_list) + ) + { + genfunc_node_ptr->init_branches(); + + const Type v = result->value(); + + details::free_node(*node_allocator_,result); + + return node_allocator_->allocate(v); + } + else if (genfunc_node_ptr->init_branches()) + { + if (result && result->valid()) + { + parser_->state_.activate_side_effect("generic_function_call()"); + return result; + } + + parser_->set_error(parser_error::make_error( + parser_error::e_synthesis, + token_t(), + "ERR266 - Failed to synthesize node: " + node_name, + exprtk_error_location)); + + details::free_node(*node_allocator_, result); + return error_node(); + } + else + { + details::free_node(*node_allocator_, result); + details::free_all_nodes(*node_allocator_, arg_list); + + return error_node(); + } + } + + #ifndef exprtk_disable_string_capabilities + inline expression_node_ptr string_function_call(igeneric_function_t* gf, + std::vector& arg_list, + const std::size_t& param_seq_index = std::numeric_limits::max()) + { + if (!all_nodes_valid(arg_list)) + { + details::free_all_nodes(*node_allocator_,arg_list); + return error_node(); + } + + typedef details::string_function_node alloc_type1; + typedef details::multimode_strfunction_node alloc_type2; + + const std::size_t no_psi = std::numeric_limits::max(); + + expression_node_ptr result = error_node(); + std::string node_name = "Unknown"; + + if (no_psi == param_seq_index) + { + result = node_allocator_->allocate(gf,arg_list); + node_name = "string_function_node"; + } + else + { + result = node_allocator_->allocate(gf, param_seq_index, arg_list); + node_name = "multimode_strfunction_node"; + } + + alloc_type1* strfunc_node_ptr = static_cast(result); + + assert(strfunc_node_ptr); + + if ( + !arg_list.empty() && + !gf->has_side_effects() && + is_constant_foldable(arg_list) + ) + { + strfunc_node_ptr->init_branches(); + + const Type v = result->value(); + + details::free_node(*node_allocator_,result); + + return node_allocator_->allocate(v); + } + else if (strfunc_node_ptr->init_branches()) + { + if (result && result->valid()) + { + parser_->state_.activate_side_effect("string_function_call()"); + return result; + } + + parser_->set_error(parser_error::make_error( + parser_error::e_synthesis, + token_t(), + "ERR267 - Failed to synthesize node: " + node_name, + exprtk_error_location)); + + details::free_node(*node_allocator_, result); + return error_node(); + } + else + { + details::free_node (*node_allocator_,result ); + details::free_all_nodes(*node_allocator_,arg_list); + + return error_node(); + } + } + #endif + + #ifndef exprtk_disable_return_statement + inline expression_node_ptr return_call(std::vector& arg_list) + { + if (!all_nodes_valid(arg_list)) + { + details::free_all_nodes(*node_allocator_,arg_list); + return error_node(); + } + + typedef details::return_node alloc_type; + + expression_node_ptr result = node_allocator_-> + allocate_rr(arg_list,parser_->results_ctx()); + + alloc_type* return_node_ptr = static_cast(result); + + assert(return_node_ptr); + + if (return_node_ptr->init_branches()) + { + if (result && result->valid()) + { + parser_->state_.activate_side_effect("return_call()"); + return result; + } + + parser_->set_error(parser_error::make_error( + parser_error::e_synthesis, + token_t(), + "ERR268 - Failed to synthesize node: return_node", + exprtk_error_location)); + + details::free_node(*node_allocator_, result); + return error_node(); + } + else + { + details::free_node (*node_allocator_, result ); + details::free_all_nodes(*node_allocator_, arg_list); + + return error_node(); + } + } + + inline expression_node_ptr return_envelope(expression_node_ptr body, + results_context_t* rc, + bool*& return_invoked) + { + typedef details::return_envelope_node alloc_type; + + expression_node_ptr result = node_allocator_-> + allocate_cr(body,(*rc)); + + return_invoked = static_cast(result)->retinvk_ptr(); + + return result; + } + #else + inline expression_node_ptr return_call(std::vector&) + { + return error_node(); + } + + inline expression_node_ptr return_envelope(expression_node_ptr, + results_context_t*, + bool*&) + { + return error_node(); + } + #endif + + inline expression_node_ptr vector_element(const std::string& symbol, + vector_holder_ptr vector_base, + expression_node_ptr vec_node, + expression_node_ptr index) + { + expression_node_ptr result = error_node(); + std::string node_name = "Unknown"; + + if (details::is_constant_node(index)) + { + const std::size_t vec_index = static_cast(details::numeric::to_int64(index->value())); + + details::free_node(*node_allocator_,index); + + if (vec_index >= vector_base->size()) + { + parser_->set_error(parser_error::make_error( + parser_error::e_parser, + token_t(), + "ERR269 - Index of " + details::to_str(vec_index) + " out of range for " + "vector '" + symbol + "' of size " + details::to_str(vector_base->size()), + exprtk_error_location)); + + details::free_node(*node_allocator_,vec_node); + + return error_node(); + } + + if (vector_base->rebaseable()) + { + vector_access_runtime_check_ptr rtc = get_vector_access_runtime_check(); + + result = (rtc) ? + node_allocator_->allocate(vec_node, vec_index, vector_base, rtc) : + node_allocator_->allocate(vec_node, vec_index, vector_base ) ; + + node_name = (rtc) ? + "rebasevector_elem_rtc_node_t" : + "rebasevector_elem_node_t" ; + + if (result && result->valid()) + { + return result; + } + + parser_->set_error(parser_error::make_error( + parser_error::e_synthesis, + token_t(), + "ERR270 - Failed to synthesize node: " + node_name + " for vector: " + symbol, + exprtk_error_location)); + + details::free_node(*node_allocator_, result); + return error_node(); + } + else if (details::is_ivector_node(vec_node) && !details::is_vector_node(vec_node)) + { + vector_access_runtime_check_ptr rtc = get_vector_access_runtime_check(); + + result = (rtc) ? + node_allocator_->allocate(vec_node, vec_index, vector_base, rtc) : + node_allocator_->allocate(vec_node, vec_index, vector_base ) ; + + node_name = (rtc) ? + "vector_elem_rtc_node_t" : + "vector_elem_node_t" ; + + if (result && result->valid()) + { + return result; + } + + parser_->set_error(parser_error::make_error( + parser_error::e_synthesis, + token_t(), + "ERR271 - Failed to synthesize node: " + node_name + " for vector: " + symbol, + exprtk_error_location)); + + details::free_node(*node_allocator_, result); + return error_node(); + } + + const scope_element& se = parser_->sem_.get_element(symbol,vec_index); + + if (se.index == vec_index) + { + result = se.var_node; + details::free_node(*node_allocator_,vec_node); + } + else + { + scope_element nse; + nse.name = symbol; + nse.active = true; + nse.ref_count = 1; + nse.type = scope_element::e_vecelem; + nse.index = vec_index; + nse.depth = parser_->state_.scope_depth; + nse.data = 0; + nse.var_node = node_allocator_->allocate((*(*vector_base)[vec_index])); + + if (!parser_->sem_.add_element(nse)) + { + parser_->set_synthesis_error("Failed to add new local vector element to SEM [1]"); + + parser_->sem_.free_element(nse); + + result = error_node(); + } + + assert(parser_->sem_.total_local_symb_size_bytes() <= parser_->settings().max_total_local_symbol_size_bytes()); + + details::free_node(*node_allocator_,vec_node); + + exprtk_debug(("vector_element() - INFO - Added new local vector element: %s\n", nse.name.c_str())); + + parser_->state_.activate_side_effect("vector_element()"); + + result = nse.var_node; + node_name = "variable_node_t"; + } + } + else + { + vector_access_runtime_check_ptr rtc = get_vector_access_runtime_check(); + + if (vector_base->rebaseable()) + { + result = (rtc) ? + node_allocator_->allocate(vec_node, index, vector_base, rtc) : + node_allocator_->allocate(vec_node, index, vector_base ) ; + + node_name = (rtc) ? + "rebasevector_elem_rtc_node_t" : + "rebasevector_elem_node_t" ; + } + else + { + result = rtc ? + node_allocator_->allocate(vec_node, index, vector_base, rtc) : + node_allocator_->allocate(vec_node, index, vector_base ) ; + + node_name = (rtc) ? + "vector_elem_rtc_node_t" : + "vector_elem_node_t" ; + } + } + + if (result && result->valid()) + { + return result; + } + + parser_->set_error(parser_error::make_error( + parser_error::e_synthesis, + token_t(), + "ERR272 - Failed to synthesize node: " + node_name, + exprtk_error_location)); + + details::free_node(*node_allocator_, result); + return error_node(); + } + + private: + + template + inline bool is_constant_foldable(NodePtr (&b)[N]) const + { + for (std::size_t i = 0; i < N; ++i) + { + if (0 == b[i]) + return false; + else if (!details::is_constant_node(b[i])) + return false; + } + + return true; + } + + template class Sequence> + inline bool is_constant_foldable(const Sequence& b) const + { + for (std::size_t i = 0; i < b.size(); ++i) + { + if (0 == b[i]) + return false; + else if (!details::is_constant_node(b[i])) + return false; + } + + return true; + } + + void lodge_assignment(symbol_type cst, expression_node_ptr node) + { + parser_->state_.activate_side_effect("lodge_assignment()"); + + if (!parser_->dec_.collect_assignments()) + return; + + std::string symbol_name; + + switch (cst) + { + case e_st_variable : symbol_name = parser_->symtab_store_ + .get_variable_name(node); + break; + + #ifndef exprtk_disable_string_capabilities + case e_st_string : symbol_name = parser_->symtab_store_ + .get_stringvar_name(node); + break; + #endif + + case e_st_vector : { + typedef details::vector_holder vector_holder_t; + + vector_holder_t& vh = static_cast(node)->vec_holder(); + + symbol_name = parser_->symtab_store_.get_vector_name(&vh); + } + break; + + case e_st_vecelem : { + typedef details::vector_holder vector_holder_t; + + vector_holder_t& vh = static_cast(node)->vec_holder(); + + symbol_name = parser_->symtab_store_.get_vector_name(&vh); + + cst = e_st_vector; + } + break; + + default : return; + } + + if (!symbol_name.empty()) + { + parser_->dec_.add_assignment(symbol_name,cst); + } + } + + const void* base_ptr(expression_node_ptr node) + { + if (node) + { + switch (node->type()) + { + case details::expression_node::e_variable: + return reinterpret_cast(&static_cast(node)->ref()); + + case details::expression_node::e_vecelem: + return reinterpret_cast(&static_cast(node)->ref()); + + case details::expression_node::e_veccelem: + return reinterpret_cast(&static_cast(node)->ref()); + + case details::expression_node::e_vecelemrtc: + return reinterpret_cast(&static_cast(node)->ref()); + + case details::expression_node::e_veccelemrtc: + return reinterpret_cast(&static_cast(node)->ref()); + + case details::expression_node::e_rbvecelem: + return reinterpret_cast(&static_cast(node)->ref()); + + case details::expression_node::e_rbvecelemrtc: + return reinterpret_cast(&static_cast(node)->ref()); + + case details::expression_node::e_rbveccelem: + return reinterpret_cast(&static_cast(node)->ref()); + + case details::expression_node::e_rbveccelemrtc: + return reinterpret_cast(&static_cast(node)->ref()); + + case details::expression_node::e_vector: + return reinterpret_cast(static_cast(node)->vec_holder().data()); + + #ifndef exprtk_disable_string_capabilities + case details::expression_node::e_stringvar: + return reinterpret_cast((static_cast(node)->base())); + + case details::expression_node::e_stringvarrng: + return reinterpret_cast((static_cast(node)->base())); + #endif + default : return reinterpret_cast(0); + } + } + + return reinterpret_cast(0); + } + + bool assign_immutable_symbol(expression_node_ptr node) + { + interval_t interval; + const void* baseptr_addr = base_ptr(node); + + exprtk_debug(("assign_immutable_symbol - base ptr addr: %p\n", baseptr_addr)); + + if (parser_->immutable_memory_map_.in_interval(baseptr_addr,interval)) + { + typename immutable_symtok_map_t::iterator itr = parser_->immutable_symtok_map_.find(interval); + + if (parser_->immutable_symtok_map_.end() != itr) + { + token_t& token = itr->second; + parser_->set_error(parser_error::make_error( + parser_error::e_parser, + token, + "ERR273 - Symbol '" + token.value + "' cannot be assigned-to as it is immutable.", + exprtk_error_location)); + } + else + parser_->set_synthesis_error("Unable to assign symbol is immutable."); + + return true; + } + + return false; + } + + inline expression_node_ptr synthesize_assignment_expression(const details::operator_type& operation, expression_node_ptr (&branch)[2]) + { + if (assign_immutable_symbol(branch[0])) + { + return error_node(); + } + else if (details::is_variable_node(branch[0])) + { + lodge_assignment(e_st_variable,branch[0]); + return synthesize_expression(operation,branch); + } + else if (details::is_vector_elem_node(branch[0]) || details::is_vector_celem_node(branch[0])) + { + lodge_assignment(e_st_vecelem,branch[0]); + return synthesize_expression(operation, branch); + } + else if (details::is_vector_elem_rtc_node(branch[0]) || details::is_vector_celem_rtc_node(branch[0])) + { + lodge_assignment(e_st_vecelem,branch[0]); + return synthesize_expression(operation, branch); + } + else if (details::is_rebasevector_elem_node(branch[0])) + { + lodge_assignment(e_st_vecelem,branch[0]); + return synthesize_expression(operation, branch); + } + else if (details::is_rebasevector_elem_rtc_node(branch[0])) + { + lodge_assignment(e_st_vecelem,branch[0]); + return synthesize_expression(operation, branch); + } + else if (details::is_rebasevector_celem_node(branch[0])) + { + lodge_assignment(e_st_vecelem,branch[0]); + return synthesize_expression(operation, branch); + } + #ifndef exprtk_disable_string_capabilities + else if (details::is_string_node(branch[0])) + { + lodge_assignment(e_st_string,branch[0]); + return synthesize_expression(operation, branch); + } + else if (details::is_string_range_node(branch[0])) + { + lodge_assignment(e_st_string,branch[0]); + return synthesize_expression(operation, branch); + } + #endif + else if (details::is_vector_node(branch[0])) + { + lodge_assignment(e_st_vector,branch[0]); + + if (details::is_ivector_node(branch[1])) + return synthesize_expression(operation, branch); + else + return synthesize_expression(operation, branch); + } + else if (details::is_literal_node(branch[0])) + { + parser_->set_error(parser_error::make_error( + parser_error::e_syntax, + parser_->current_state().token, + "ERR274 - Cannot assign value to const variable", + exprtk_error_location)); + + return error_node(); + } + else + { + parser_->set_error(parser_error::make_error( + parser_error::e_syntax, + parser_->current_state().token, + "ERR275 - Invalid branches for assignment operator '" + details::to_str(operation) + "'", + exprtk_error_location)); + + return error_node(); + } + } + + inline expression_node_ptr synthesize_assignment_operation_expression(const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + if (assign_immutable_symbol(branch[0])) + { + return error_node(); + } + + expression_node_ptr result = error_node(); + std::string node_name = "Unknown"; + + if (details::is_variable_node(branch[0])) + { + lodge_assignment(e_st_variable,branch[0]); + + switch (operation) + { + #define case_stmt(op0, op1) \ + case op0 : result = node_allocator_-> \ + template allocate_rrr > > \ + (operation, branch[0], branch[1]); \ + node_name = "assignment_op_node"; \ + break; \ + + case_stmt(details::e_addass , details::add_op) + case_stmt(details::e_subass , details::sub_op) + case_stmt(details::e_mulass , details::mul_op) + case_stmt(details::e_divass , details::div_op) + case_stmt(details::e_modass , details::mod_op) + #undef case_stmt + default : return error_node(); + } + } + else if (details::is_vector_elem_node(branch[0])) + { + lodge_assignment(e_st_vecelem,branch[0]); + + switch (operation) + { + #define case_stmt(op0, op1) \ + case op0 : result = node_allocator_-> \ + template allocate_rrr > > \ + (operation, branch[0], branch[1]); \ + node_name = "assignment_vec_elem_op_node"; \ + break; \ + + case_stmt(details::e_addass , details::add_op) + case_stmt(details::e_subass , details::sub_op) + case_stmt(details::e_mulass , details::mul_op) + case_stmt(details::e_divass , details::div_op) + case_stmt(details::e_modass , details::mod_op) + #undef case_stmt + default : return error_node(); + } + } + else if (details::is_vector_elem_rtc_node(branch[0])) + { + lodge_assignment(e_st_vecelem,branch[0]); + + switch (operation) + { + #define case_stmt(op0, op1) \ + case op0 : result = node_allocator_-> \ + template allocate_rrr > > \ + (operation, branch[0], branch[1]); \ + node_name = "assignment_vec_elem_op_rtc_node"; \ + break; \ + + case_stmt(details::e_addass , details::add_op) + case_stmt(details::e_subass , details::sub_op) + case_stmt(details::e_mulass , details::mul_op) + case_stmt(details::e_divass , details::div_op) + case_stmt(details::e_modass , details::mod_op) + #undef case_stmt + default : return error_node(); + } + } + else if (details::is_vector_celem_rtc_node(branch[0])) + { + lodge_assignment(e_st_vecelem,branch[0]); + + switch (operation) + { + #define case_stmt(op0, op1) \ + case op0 : result = node_allocator_-> \ + template allocate_rrr > > \ + (operation, branch[0], branch[1]); \ + node_name = "assignment_vec_celem_op_rtc_node"; \ + break; \ + + case_stmt(details::e_addass , details::add_op) + case_stmt(details::e_subass , details::sub_op) + case_stmt(details::e_mulass , details::mul_op) + case_stmt(details::e_divass , details::div_op) + case_stmt(details::e_modass , details::mod_op) + #undef case_stmt + default : return error_node(); + } + } + else if (details::is_rebasevector_elem_node(branch[0])) + { + lodge_assignment(e_st_vecelem,branch[0]); + + switch (operation) + { + #define case_stmt(op0, op1) \ + case op0 : result = node_allocator_-> \ + template allocate_rrr > > \ + (operation, branch[0], branch[1]); \ + node_name = "assignment_rebasevec_elem_op_node"; \ + break; \ + + case_stmt(details::e_addass , details::add_op) + case_stmt(details::e_subass , details::sub_op) + case_stmt(details::e_mulass , details::mul_op) + case_stmt(details::e_divass , details::div_op) + case_stmt(details::e_modass , details::mod_op) + #undef case_stmt + default : return error_node(); + } + } + else if (details::is_rebasevector_celem_node(branch[0])) + { + lodge_assignment(e_st_vecelem,branch[0]); + + switch (operation) + { + #define case_stmt(op0, op1) \ + case op0 : result = node_allocator_-> \ + template allocate_rrr > > \ + (operation, branch[0], branch[1]); \ + node_name = "assignment_rebasevec_celem_op_node"; \ + break; \ + + case_stmt(details::e_addass , details::add_op) + case_stmt(details::e_subass , details::sub_op) + case_stmt(details::e_mulass , details::mul_op) + case_stmt(details::e_divass , details::div_op) + case_stmt(details::e_modass , details::mod_op) + #undef case_stmt + default : return error_node(); + } + } + else if (details::is_rebasevector_elem_rtc_node(branch[0])) + { + lodge_assignment(e_st_vecelem,branch[0]); + + switch (operation) + { + #define case_stmt(op0, op1) \ + case op0 : result = node_allocator_-> \ + template allocate_rrr > > \ + (operation, branch[0], branch[1]); \ + node_name = "assignment_rebasevec_elem_op_rtc_node"; \ + break; \ + + case_stmt(details::e_addass , details::add_op) + case_stmt(details::e_subass , details::sub_op) + case_stmt(details::e_mulass , details::mul_op) + case_stmt(details::e_divass , details::div_op) + case_stmt(details::e_modass , details::mod_op) + #undef case_stmt + default : return error_node(); + } + } + else if (details::is_rebasevector_celem_rtc_node(branch[0])) + { + lodge_assignment(e_st_vecelem,branch[0]); + + switch (operation) + { + #define case_stmt(op0, op1) \ + case op0 : result = node_allocator_-> \ + template allocate_rrr > > \ + (operation, branch[0], branch[1]); \ + node_name = "assignment_rebasevec_celem_op_rtc_node"; \ + break; \ + + case_stmt(details::e_addass , details::add_op) + case_stmt(details::e_subass , details::sub_op) + case_stmt(details::e_mulass , details::mul_op) + case_stmt(details::e_divass , details::div_op) + case_stmt(details::e_modass , details::mod_op) + #undef case_stmt + default : return error_node(); + } + } + else if (details::is_vector_node(branch[0])) + { + lodge_assignment(e_st_vector,branch[0]); + + if (details::is_ivector_node(branch[1])) + { + switch (operation) + { + #define case_stmt(op0, op1) \ + case op0 : result = node_allocator_-> \ + template allocate_rrr > > \ + (operation, branch[0], branch[1]); \ + node_name = "assignment_rebasevec_celem_op_node"; \ + break; \ + + case_stmt(details::e_addass , details::add_op) + case_stmt(details::e_subass , details::sub_op) + case_stmt(details::e_mulass , details::mul_op) + case_stmt(details::e_divass , details::div_op) + case_stmt(details::e_modass , details::mod_op) + #undef case_stmt + default : return error_node(); + } + } + else + { + switch (operation) + { + #define case_stmt(op0, op1) \ + case op0 : result = node_allocator_-> \ + template allocate_rrr > > \ + (operation, branch[0], branch[1]); \ + node_name = "assignment_vec_op_node"; \ + break; \ + + case_stmt(details::e_addass , details::add_op) + case_stmt(details::e_subass , details::sub_op) + case_stmt(details::e_mulass , details::mul_op) + case_stmt(details::e_divass , details::div_op) + case_stmt(details::e_modass , details::mod_op) + #undef case_stmt + default : return error_node(); + } + } + } + #ifndef exprtk_disable_string_capabilities + else if ( + (details::e_addass == operation) && + details::is_string_node(branch[0]) + ) + { + typedef details::assignment_string_node addass_t; + + lodge_assignment(e_st_string,branch[0]); + + result = synthesize_expression(operation,branch); + node_name = "assignment_string_node"; + } + #endif + else + { + parser_->set_error(parser_error::make_error( + parser_error::e_syntax, + parser_->current_state().token, + "ERR276 - Invalid branches for assignment operator '" + details::to_str(operation) + "'", + exprtk_error_location)); + + return error_node(); + } + + if (result && result->valid()) + { + return result; + } + + parser_->set_error(parser_error::make_error( + parser_error::e_synthesis, + token_t(), + "ERR277 - Failed to synthesize node: " + node_name, + exprtk_error_location)); + + details::free_node(*node_allocator_, result); + return error_node(); + } + + inline expression_node_ptr synthesize_veceqineqlogic_operation_expression(const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + const bool is_b0_ivec = details::is_ivector_node(branch[0]); + const bool is_b1_ivec = details::is_ivector_node(branch[1]); + + #define batch_eqineq_logic_case \ + case_stmt(details::e_lt , details::lt_op ) \ + case_stmt(details::e_lte , details::lte_op ) \ + case_stmt(details::e_gt , details::gt_op ) \ + case_stmt(details::e_gte , details::gte_op ) \ + case_stmt(details::e_eq , details::eq_op ) \ + case_stmt(details::e_ne , details::ne_op ) \ + case_stmt(details::e_equal , details::equal_op) \ + case_stmt(details::e_and , details::and_op ) \ + case_stmt(details::e_nand , details::nand_op ) \ + case_stmt(details::e_or , details::or_op ) \ + case_stmt(details::e_nor , details::nor_op ) \ + case_stmt(details::e_xor , details::xor_op ) \ + case_stmt(details::e_xnor , details::xnor_op ) \ + + expression_node_ptr result = error_node(); + std::string node_name = "Unknown"; + + if (is_b0_ivec && is_b1_ivec) + { + switch (operation) + { + #define case_stmt(op0, op1) \ + case op0 : result = node_allocator_-> \ + template allocate_rrr > > \ + (operation, branch[0], branch[1]); \ + node_name = "vec_binop_vecvec_node"; \ + break; \ + + batch_eqineq_logic_case + #undef case_stmt + default : return error_node(); + } + } + else if (is_b0_ivec && !is_b1_ivec) + { + switch (operation) + { + #define case_stmt(op0, op1) \ + case op0 : result = node_allocator_-> \ + template allocate_rrr > > \ + (operation, branch[0], branch[1]); \ + node_name = "vec_binop_vecval_node"; \ + break; \ + + batch_eqineq_logic_case + #undef case_stmt + default : return error_node(); + } + } + else if (!is_b0_ivec && is_b1_ivec) + { + switch (operation) + { + #define case_stmt(op0, op1) \ + case op0 : result = node_allocator_-> \ + template allocate_rrr > > \ + (operation, branch[0], branch[1]); \ + node_name = "vec_binop_valvec_node"; \ + break; \ + + batch_eqineq_logic_case + #undef case_stmt + default : return error_node(); + } + } + else + return error_node(); + + if (result && result->valid()) + { + return result; + } + + parser_->set_error(parser_error::make_error( + parser_error::e_synthesis, + token_t(), + "ERR278 - Failed to synthesize node: " + node_name, + exprtk_error_location)); + + details::free_node(*node_allocator_, result); + return error_node(); + + #undef batch_eqineq_logic_case + } + + inline expression_node_ptr synthesize_vecarithmetic_operation_expression(const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + const bool is_b0_ivec = details::is_ivector_node(branch[0]); + const bool is_b1_ivec = details::is_ivector_node(branch[1]); + + #define vector_ops \ + case_stmt(details::e_add , details::add_op) \ + case_stmt(details::e_sub , details::sub_op) \ + case_stmt(details::e_mul , details::mul_op) \ + case_stmt(details::e_div , details::div_op) \ + case_stmt(details::e_mod , details::mod_op) \ + + expression_node_ptr result = error_node(); + std::string node_name = "Unknown"; + + if (is_b0_ivec && is_b1_ivec) + { + switch (operation) + { + #define case_stmt(op0, op1) \ + case op0 : result = node_allocator_-> \ + template allocate_rrr > > \ + (operation, branch[0], branch[1]); \ + node_name = "vec_binop_vecvec_node"; \ + break; \ + + vector_ops + case_stmt(details::e_pow,details:: pow_op) + #undef case_stmt + default : return error_node(); + } + } + else if (is_b0_ivec && !is_b1_ivec) + { + switch (operation) + { + #define case_stmt(op0, op1) \ + case op0 : result = node_allocator_-> \ + template allocate_rrr > > \ + (operation, branch[0], branch[1]); \ + node_name = "vec_binop_vecval_node(b0ivec,!b1ivec)"; \ + break; \ + + vector_ops + case_stmt(details::e_pow,details:: pow_op) + #undef case_stmt + default : return error_node(); + } + } + else if (!is_b0_ivec && is_b1_ivec) + { + switch (operation) + { + #define case_stmt(op0, op1) \ + case op0 : result = node_allocator_-> \ + template allocate_rrr > > \ + (operation, branch[0], branch[1]); \ + node_name = "vec_binop_vecval_node(!b0ivec,b1ivec)"; \ + break; \ + + vector_ops + #undef case_stmt + default : return error_node(); + } + } + else + return error_node(); + + if (result && result->valid()) + { + return result; + } + + parser_->set_error(parser_error::make_error( + parser_error::e_synthesis, + token_t(), + "ERR279 - Failed to synthesize node: " + node_name, + exprtk_error_location)); + + details::free_node(*node_allocator_, result); + return error_node(); + + #undef vector_ops + } + + inline expression_node_ptr synthesize_swap_expression(expression_node_ptr (&branch)[2]) + { + const bool v0_is_ivar = details::is_ivariable_node(branch[0]); + const bool v1_is_ivar = details::is_ivariable_node(branch[1]); + + const bool v0_is_ivec = details::is_ivector_node (branch[0]); + const bool v1_is_ivec = details::is_ivector_node (branch[1]); + + #ifndef exprtk_disable_string_capabilities + const bool v0_is_str = details::is_generally_string_node(branch[0]); + const bool v1_is_str = details::is_generally_string_node(branch[1]); + #endif + + expression_node_ptr result = error_node(); + std::string node_name = "Unknown"; + + if (v0_is_ivar && v1_is_ivar) + { + typedef details::variable_node* variable_node_ptr; + + variable_node_ptr v0 = variable_node_ptr(0); + variable_node_ptr v1 = variable_node_ptr(0); + + if ( + (0 != (v0 = dynamic_cast(branch[0]))) && + (0 != (v1 = dynamic_cast(branch[1]))) + ) + { + result = node_allocator_->allocate >(v0,v1); + node_name = "swap_node"; + } + else + { + result = node_allocator_->allocate >(branch[0],branch[1]); + node_name = "swap_generic_node"; + } + } + else if (v0_is_ivec && v1_is_ivec) + { + result = node_allocator_->allocate >(branch[0],branch[1]); + node_name = "swap_vecvec_node"; + } + #ifndef exprtk_disable_string_capabilities + else if (v0_is_str && v1_is_str) + { + if (is_string_node(branch[0]) && is_string_node(branch[1])) + { + result = node_allocator_->allocate > + (branch[0], branch[1]); + node_name = "swap_string_node"; + } + else + { + result = node_allocator_->allocate > + (branch[0], branch[1]); + node_name = "swap_genstrings_node"; + } + } + #endif + else + { + parser_->set_synthesis_error("Only variables, strings, vectors or vector elements can be swapped"); + return error_node(); + } + + if (result && result->valid()) + { + parser_->state_.activate_side_effect("synthesize_swap_expression()"); + return result; + } + + parser_->set_error(parser_error::make_error( + parser_error::e_synthesis, + token_t(), + "ERR280 - Failed to synthesize node: " + node_name, + exprtk_error_location)); + + details::free_node(*node_allocator_, result); + return error_node(); + } + + #ifndef exprtk_disable_sc_andor + inline expression_node_ptr synthesize_shortcircuit_expression(const details::operator_type& operation, expression_node_ptr (&branch)[2]) + { + expression_node_ptr result = error_node(); + + if (details::is_constant_node(branch[0])) + { + if ( + (details::e_scand == operation) && + std::equal_to()(T(0),branch[0]->value()) + ) + result = node_allocator_->allocate_c(T(0)); + else if ( + (details::e_scor == operation) && + std::not_equal_to()(T(0),branch[0]->value()) + ) + result = node_allocator_->allocate_c(T(1)); + } + + if (details::is_constant_node(branch[1]) && (0 == result)) + { + if ( + (details::e_scand == operation) && + std::equal_to()(T(0),branch[1]->value()) + ) + result = node_allocator_->allocate_c(T(0)); + else if ( + (details::e_scor == operation) && + std::not_equal_to()(T(0),branch[1]->value()) + ) + result = node_allocator_->allocate_c(T(1)); + } + + if (result) + { + details::free_node(*node_allocator_, branch[0]); + details::free_node(*node_allocator_, branch[1]); + + return result; + } + else if (details::e_scand == operation) + { + return synthesize_expression(operation, branch); + } + else if (details::e_scor == operation) + { + return synthesize_expression(operation, branch); + } + else + return error_node(); + } + #else + inline expression_node_ptr synthesize_shortcircuit_expression(const details::operator_type&, expression_node_ptr (&)[2]) + { + return error_node(); + } + #endif + + #define basic_opr_switch_statements \ + case_stmt(details::e_add , details::add_op) \ + case_stmt(details::e_sub , details::sub_op) \ + case_stmt(details::e_mul , details::mul_op) \ + case_stmt(details::e_div , details::div_op) \ + case_stmt(details::e_mod , details::mod_op) \ + case_stmt(details::e_pow , details::pow_op) \ + + #define extended_opr_switch_statements \ + case_stmt(details::e_lt , details::lt_op ) \ + case_stmt(details::e_lte , details::lte_op ) \ + case_stmt(details::e_gt , details::gt_op ) \ + case_stmt(details::e_gte , details::gte_op ) \ + case_stmt(details::e_eq , details::eq_op ) \ + case_stmt(details::e_ne , details::ne_op ) \ + case_stmt(details::e_and , details::and_op ) \ + case_stmt(details::e_nand , details::nand_op) \ + case_stmt(details::e_or , details::or_op ) \ + case_stmt(details::e_nor , details::nor_op ) \ + case_stmt(details::e_xor , details::xor_op ) \ + case_stmt(details::e_xnor , details::xnor_op) \ + + #ifndef exprtk_disable_cardinal_pow_optimisation + template class IPowNode> + inline expression_node_ptr cardinal_pow_optimisation_impl(const TType& v, const unsigned int& p) + { + switch (p) + { + #define case_stmt(cp) \ + case cp : return node_allocator_-> \ + allocate > >(v); \ + + case_stmt( 1) case_stmt( 2) case_stmt( 3) case_stmt( 4) + case_stmt( 5) case_stmt( 6) case_stmt( 7) case_stmt( 8) + case_stmt( 9) case_stmt(10) case_stmt(11) case_stmt(12) + case_stmt(13) case_stmt(14) case_stmt(15) case_stmt(16) + case_stmt(17) case_stmt(18) case_stmt(19) case_stmt(20) + case_stmt(21) case_stmt(22) case_stmt(23) case_stmt(24) + case_stmt(25) case_stmt(26) case_stmt(27) case_stmt(28) + case_stmt(29) case_stmt(30) case_stmt(31) case_stmt(32) + case_stmt(33) case_stmt(34) case_stmt(35) case_stmt(36) + case_stmt(37) case_stmt(38) case_stmt(39) case_stmt(40) + case_stmt(41) case_stmt(42) case_stmt(43) case_stmt(44) + case_stmt(45) case_stmt(46) case_stmt(47) case_stmt(48) + case_stmt(49) case_stmt(50) case_stmt(51) case_stmt(52) + case_stmt(53) case_stmt(54) case_stmt(55) case_stmt(56) + case_stmt(57) case_stmt(58) case_stmt(59) case_stmt(60) + #undef case_stmt + default : return error_node(); + } + } + + inline expression_node_ptr cardinal_pow_optimisation(const T& v, const T& c) + { + const bool not_recipricol = (c >= T(0)); + const unsigned int p = static_cast(details::numeric::to_int32(details::numeric::abs(c))); + + if (0 == p) + return node_allocator_->allocate_c(T(1)); + else if (std::equal_to()(T(2),c)) + { + return node_allocator_-> + template allocate_rr > >(v,v); + } + else + { + if (not_recipricol) + return cardinal_pow_optimisation_impl(v,p); + else + return cardinal_pow_optimisation_impl(v,p); + } + } + + inline bool cardinal_pow_optimisable(const details::operator_type& operation, const T& c) const + { + return (details::e_pow == operation) && (details::numeric::abs(c) <= T(60)) && details::numeric::is_integer(c); + } + + inline expression_node_ptr cardinal_pow_optimisation(expression_node_ptr (&branch)[2]) + { + const Type c = static_cast*>(branch[1])->value(); + const bool not_recipricol = (c >= T(0)); + const unsigned int p = static_cast(details::numeric::to_int32(details::numeric::abs(c))); + + node_allocator_->free(branch[1]); + + if (0 == p) + { + details::free_all_nodes(*node_allocator_, branch); + + return node_allocator_->allocate_c(T(1)); + } + else if (not_recipricol) + return cardinal_pow_optimisation_impl(branch[0],p); + else + return cardinal_pow_optimisation_impl(branch[0],p); + } + #else + inline expression_node_ptr cardinal_pow_optimisation(T&, const T&) + { + return error_node(); + } + + inline bool cardinal_pow_optimisable(const details::operator_type&, const T&) + { + return false; + } + + inline expression_node_ptr cardinal_pow_optimisation(expression_node_ptr(&)[2]) + { + return error_node(); + } + #endif + + struct synthesize_binary_ext_expression + { + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + const bool left_neg = is_neg_unary_node(branch[0]); + const bool right_neg = is_neg_unary_node(branch[1]); + + if (left_neg && right_neg) + { + if ( + (details::e_add == operation) || + (details::e_sub == operation) || + (details::e_mul == operation) || + (details::e_div == operation) + ) + { + if ( + !expr_gen.parser_->simplify_unary_negation_branch(branch[0]) || + !expr_gen.parser_->simplify_unary_negation_branch(branch[1]) + ) + { + details::free_all_nodes(*expr_gen.node_allocator_,branch); + + return error_node(); + } + } + + switch (operation) + { + // -f(x + 1) + -g(y + 1) --> -(f(x + 1) + g(y + 1)) + case details::e_add : return expr_gen(details::e_neg, + expr_gen.node_allocator_-> + template allocate > > + (branch[0],branch[1])); + + // -f(x + 1) - -g(y + 1) --> g(y + 1) - f(x + 1) + case details::e_sub : return expr_gen.node_allocator_-> + template allocate > > + (branch[1],branch[0]); + + default : break; + } + } + else if (left_neg && !right_neg) + { + if ( + (details::e_add == operation) || + (details::e_sub == operation) || + (details::e_mul == operation) || + (details::e_div == operation) + ) + { + if (!expr_gen.parser_->simplify_unary_negation_branch(branch[0])) + { + details::free_all_nodes(*expr_gen.node_allocator_,branch); + + return error_node(); + } + + switch (operation) + { + // -f(x + 1) + g(y + 1) --> g(y + 1) - f(x + 1) + case details::e_add : return expr_gen.node_allocator_-> + template allocate > > + (branch[1], branch[0]); + + // -f(x + 1) - g(y + 1) --> -(f(x + 1) + g(y + 1)) + case details::e_sub : return expr_gen(details::e_neg, + expr_gen.node_allocator_-> + template allocate > > + (branch[0], branch[1])); + + // -f(x + 1) * g(y + 1) --> -(f(x + 1) * g(y + 1)) + case details::e_mul : return expr_gen(details::e_neg, + expr_gen.node_allocator_-> + template allocate > > + (branch[0], branch[1])); + + // -f(x + 1) / g(y + 1) --> -(f(x + 1) / g(y + 1)) + case details::e_div : return expr_gen(details::e_neg, + expr_gen.node_allocator_-> + template allocate > > + (branch[0], branch[1])); + + default : return error_node(); + } + } + } + else if (!left_neg && right_neg) + { + if ( + (details::e_add == operation) || + (details::e_sub == operation) || + (details::e_mul == operation) || + (details::e_div == operation) + ) + { + if (!expr_gen.parser_->simplify_unary_negation_branch(branch[1])) + { + details::free_all_nodes(*expr_gen.node_allocator_,branch); + + return error_node(); + } + + switch (operation) + { + // f(x + 1) + -g(y + 1) --> f(x + 1) - g(y + 1) + case details::e_add : return expr_gen.node_allocator_-> + template allocate > > + (branch[0], branch[1]); + + // f(x + 1) - - g(y + 1) --> f(x + 1) + g(y + 1) + case details::e_sub : return expr_gen.node_allocator_-> + template allocate > > + (branch[0], branch[1]); + + // f(x + 1) * -g(y + 1) --> -(f(x + 1) * g(y + 1)) + case details::e_mul : return expr_gen(details::e_neg, + expr_gen.node_allocator_-> + template allocate > > + (branch[0], branch[1])); + + // f(x + 1) / -g(y + 1) --> -(f(x + 1) / g(y + 1)) + case details::e_div : return expr_gen(details::e_neg, + expr_gen.node_allocator_-> + template allocate > > + (branch[0], branch[1])); + + default : return error_node(); + } + } + } + + switch (operation) + { + #define case_stmt(op0, op1) \ + case op0 : return expr_gen.node_allocator_-> \ + template allocate > > \ + (branch[0], branch[1]); \ + + basic_opr_switch_statements + extended_opr_switch_statements + #undef case_stmt + default : return error_node(); + } + } + }; + + struct synthesize_vob_expression + { + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + const Type& v = static_cast*>(branch[0])->ref(); + + #ifndef exprtk_disable_enhanced_features + if (details::is_sf3ext_node(branch[1])) + { + expression_node_ptr result = error_node(); + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile_right + (expr_gen, v, operation, branch[1], result); + + if (synthesis_result) + { + details::free_node(*expr_gen.node_allocator_,branch[1]); + return result; + } + } + #endif + + if ( + (details::e_mul == operation) || + (details::e_div == operation) + ) + { + if (details::is_uv_node(branch[1])) + { + typedef details::uv_base_node* uvbn_ptr_t; + + details::operator_type o = static_cast(branch[1])->operation(); + + if (details::e_neg == o) + { + const Type& v1 = static_cast(branch[1])->v(); + + details::free_node(*expr_gen.node_allocator_,branch[1]); + + switch (operation) + { + case details::e_mul : return expr_gen(details::e_neg, + expr_gen.node_allocator_-> + template allocate_rr > >(v,v1)); + + case details::e_div : return expr_gen(details::e_neg, + expr_gen.node_allocator_-> + template allocate_rr > >(v,v1)); + + default : break; + } + } + } + } + + switch (operation) + { + #define case_stmt(op0, op1) \ + case op0 : return expr_gen.node_allocator_-> \ + template allocate_rc > > \ + (v, branch[1]); \ + + basic_opr_switch_statements + extended_opr_switch_statements + #undef case_stmt + default : return error_node(); + } + } + }; + + struct synthesize_bov_expression + { + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + const Type& v = static_cast*>(branch[1])->ref(); + + #ifndef exprtk_disable_enhanced_features + if (details::is_sf3ext_node(branch[0])) + { + expression_node_ptr result = error_node(); + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile_left + (expr_gen, v, operation, branch[0], result); + + if (synthesis_result) + { + details::free_node(*expr_gen.node_allocator_, branch[0]); + + return result; + } + } + #endif + + if ( + (details::e_add == operation) || + (details::e_sub == operation) || + (details::e_mul == operation) || + (details::e_div == operation) + ) + { + if (details::is_uv_node(branch[0])) + { + typedef details::uv_base_node* uvbn_ptr_t; + + details::operator_type o = static_cast(branch[0])->operation(); + + if (details::e_neg == o) + { + const Type& v0 = static_cast(branch[0])->v(); + + details::free_node(*expr_gen.node_allocator_,branch[0]); + + switch (operation) + { + case details::e_add : return expr_gen.node_allocator_-> + template allocate_rr > >(v,v0); + + case details::e_sub : return expr_gen(details::e_neg, + expr_gen.node_allocator_-> + template allocate_rr > >(v0,v)); + + case details::e_mul : return expr_gen(details::e_neg, + expr_gen.node_allocator_-> + template allocate_rr > >(v0,v)); + + case details::e_div : return expr_gen(details::e_neg, + expr_gen.node_allocator_-> + template allocate_rr > >(v0,v)); + default : break; + } + } + } + } + + switch (operation) + { + #define case_stmt(op0, op1) \ + case op0 : return expr_gen.node_allocator_-> \ + template allocate_cr > > \ + (branch[0], v); \ + + basic_opr_switch_statements + extended_opr_switch_statements + #undef case_stmt + default : return error_node(); + } + } + }; + + struct synthesize_cob_expression + { + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + const Type c = static_cast*>(branch[0])->value(); + + details::free_node(*expr_gen.node_allocator_,branch[0]); + + if (std::equal_to()(T(0),c) && (details::e_mul == operation)) + { + details::free_node(*expr_gen.node_allocator_,branch[1]); + + return expr_gen(T(0)); + } + else if (std::equal_to()(T(0),c) && (details::e_div == operation)) + { + details::free_node(*expr_gen.node_allocator_, branch[1]); + + return expr_gen(T(0)); + } + else if (std::equal_to()(T(0),c) && (details::e_add == operation)) + return branch[1]; + else if (std::equal_to()(T(1),c) && (details::e_mul == operation)) + return branch[1]; + + if (details::is_cob_node(branch[1])) + { + // Simplify expressions of the form: + // 1. (1 * (2 * (3 * (4 * (5 * (6 * (7 * (8 * (9 + x))))))))) --> 40320 * (9 + x) + // 2. (1 + (2 + (3 + (4 + (5 + (6 + (7 + (8 + (9 + x))))))))) --> 45 + x + if ( + (details::e_mul == operation) || + (details::e_add == operation) + ) + { + details::cob_base_node* cobnode = static_cast*>(branch[1]); + + if (operation == cobnode->operation()) + { + switch (operation) + { + case details::e_add : cobnode->set_c(c + cobnode->c()); break; + case details::e_mul : cobnode->set_c(c * cobnode->c()); break; + default : return error_node(); + } + + return cobnode; + } + } + + if (operation == details::e_mul) + { + details::cob_base_node* cobnode = static_cast*>(branch[1]); + details::operator_type cob_opr = cobnode->operation(); + + if ( + (details::e_div == cob_opr) || + (details::e_mul == cob_opr) + ) + { + switch (cob_opr) + { + case details::e_div : cobnode->set_c(c * cobnode->c()); break; + case details::e_mul : cobnode->set_c(cobnode->c() / c); break; + default : return error_node(); + } + + return cobnode; + } + } + else if (operation == details::e_div) + { + details::cob_base_node* cobnode = static_cast*>(branch[1]); + details::operator_type cob_opr = cobnode->operation(); + + if ( + (details::e_div == cob_opr) || + (details::e_mul == cob_opr) + ) + { + details::expression_node* new_cobnode = error_node(); + + switch (cob_opr) + { + case details::e_div : new_cobnode = expr_gen.node_allocator_-> + template allocate_tt > > + (c / cobnode->c(), cobnode->move_branch(0)); + break; + + case details::e_mul : new_cobnode = expr_gen.node_allocator_-> + template allocate_tt > > + (c / cobnode->c(), cobnode->move_branch(0)); + break; + + default : return error_node(); + } + + details::free_node(*expr_gen.node_allocator_,branch[1]); + + return new_cobnode; + } + } + } + #ifndef exprtk_disable_enhanced_features + else if (details::is_sf3ext_node(branch[1])) + { + expression_node_ptr result = error_node(); + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile_right + (expr_gen, c, operation, branch[1], result); + + if (synthesis_result) + { + details::free_node(*expr_gen.node_allocator_,branch[1]); + + return result; + } + } + #endif + + switch (operation) + { + #define case_stmt(op0, op1) \ + case op0 : return expr_gen.node_allocator_-> \ + template allocate_tt > > \ + (c, branch[1]); \ + + basic_opr_switch_statements + extended_opr_switch_statements + #undef case_stmt + default : return error_node(); + } + } + }; + + struct synthesize_boc_expression + { + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + const Type c = static_cast*>(branch[1])->value(); + + details::free_node(*(expr_gen.node_allocator_), branch[1]); + + if (std::equal_to()(T(0),c) && (details::e_mul == operation)) + { + details::free_node(*expr_gen.node_allocator_, branch[0]); + + return expr_gen(T(0)); + } + else if (std::equal_to()(T(0),c) && (details::e_div == operation)) + { + details::free_node(*expr_gen.node_allocator_, branch[0]); + + return expr_gen(std::numeric_limits::quiet_NaN()); + } + else if (std::equal_to()(T(0),c) && (details::e_add == operation)) + return branch[0]; + else if (std::equal_to()(T(1),c) && (details::e_mul == operation)) + return branch[0]; + + if (details::is_boc_node(branch[0])) + { + // Simplify expressions of the form: + // 1. (((((((((x + 9) * 8) * 7) * 6) * 5) * 4) * 3) * 2) * 1) --> (x + 9) * 40320 + // 2. (((((((((x + 9) + 8) + 7) + 6) + 5) + 4) + 3) + 2) + 1) --> x + 45 + if ( + (details::e_mul == operation) || + (details::e_add == operation) + ) + { + details::boc_base_node* bocnode = static_cast*>(branch[0]); + + if (operation == bocnode->operation()) + { + switch (operation) + { + case details::e_add : bocnode->set_c(c + bocnode->c()); break; + case details::e_mul : bocnode->set_c(c * bocnode->c()); break; + default : return error_node(); + } + + return bocnode; + } + } + else if (operation == details::e_div) + { + details::boc_base_node* bocnode = static_cast*>(branch[0]); + details::operator_type boc_opr = bocnode->operation(); + + if ( + (details::e_div == boc_opr) || + (details::e_mul == boc_opr) + ) + { + switch (boc_opr) + { + case details::e_div : bocnode->set_c(c * bocnode->c()); break; + case details::e_mul : bocnode->set_c(bocnode->c() / c); break; + default : return error_node(); + } + + return bocnode; + } + } + else if (operation == details::e_pow) + { + // (v ^ c0) ^ c1 --> v ^(c0 * c1) + details::boc_base_node* bocnode = static_cast*>(branch[0]); + details::operator_type boc_opr = bocnode->operation(); + + if (details::e_pow == boc_opr) + { + bocnode->set_c(bocnode->c() * c); + + return bocnode; + } + } + } + + #ifndef exprtk_disable_enhanced_features + if (details::is_sf3ext_node(branch[0])) + { + expression_node_ptr result = error_node(); + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile_left + (expr_gen, c, operation, branch[0], result); + + if (synthesis_result) + { + free_node(*expr_gen.node_allocator_, branch[0]); + + return result; + } + } + #endif + + switch (operation) + { + #define case_stmt(op0, op1) \ + case op0 : return expr_gen.node_allocator_-> \ + template allocate_cr > > \ + (branch[0], c); \ + + basic_opr_switch_statements + extended_opr_switch_statements + #undef case_stmt + default : return error_node(); + } + } + }; + + struct synthesize_cocob_expression + { + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + expression_node_ptr result = error_node(); + + // (cob) o c --> cob + if (details::is_cob_node(branch[0])) + { + details::cob_base_node* cobnode = static_cast*>(branch[0]); + + const Type c = static_cast*>(branch[1])->value(); + + if (std::equal_to()(T(0),c) && (details::e_mul == operation)) + { + details::free_node(*expr_gen.node_allocator_, branch[0]); + details::free_node(*expr_gen.node_allocator_, branch[1]); + + return expr_gen(T(0)); + } + else if (std::equal_to()(T(0),c) && (details::e_div == operation)) + { + details::free_node(*expr_gen.node_allocator_, branch[0]); + details::free_node(*expr_gen.node_allocator_, branch[1]); + + return expr_gen(T(std::numeric_limits::quiet_NaN())); + } + else if (std::equal_to()(T(0),c) && (details::e_add == operation)) + { + details::free_node(*expr_gen.node_allocator_, branch[1]); + + return branch[0]; + } + else if (std::equal_to()(T(1),c) && (details::e_mul == operation)) + { + details::free_node(*expr_gen.node_allocator_, branch[1]); + + return branch[0]; + } + else if (std::equal_to()(T(1),c) && (details::e_div == operation)) + { + details::free_node(*expr_gen.node_allocator_, branch[1]); + + return branch[0]; + } + + const bool op_addsub = (details::e_add == cobnode->operation()) || + (details::e_sub == cobnode->operation()) ; + + if (op_addsub) + { + switch (operation) + { + case details::e_add : cobnode->set_c(cobnode->c() + c); break; + case details::e_sub : cobnode->set_c(cobnode->c() - c); break; + default : return error_node(); + } + + result = cobnode; + } + else if (details::e_mul == cobnode->operation()) + { + switch (operation) + { + case details::e_mul : cobnode->set_c(cobnode->c() * c); break; + case details::e_div : cobnode->set_c(cobnode->c() / c); break; + default : return error_node(); + } + + result = cobnode; + } + else if (details::e_div == cobnode->operation()) + { + if (details::e_mul == operation) + { + cobnode->set_c(cobnode->c() * c); + result = cobnode; + } + else if (details::e_div == operation) + { + result = expr_gen.node_allocator_-> + template allocate_tt > > + (cobnode->c() / c, cobnode->move_branch(0)); + + details::free_node(*expr_gen.node_allocator_, branch[0]); + } + } + + if (result) + { + details::free_node(*expr_gen.node_allocator_,branch[1]); + } + } + + // c o (cob) --> cob + else if (details::is_cob_node(branch[1])) + { + details::cob_base_node* cobnode = static_cast*>(branch[1]); + + const Type c = static_cast*>(branch[0])->value(); + + if (std::equal_to()(T(0),c) && (details::e_mul == operation)) + { + details::free_node(*expr_gen.node_allocator_, branch[0]); + details::free_node(*expr_gen.node_allocator_, branch[1]); + + return expr_gen(T(0)); + } + else if (std::equal_to()(T(0),c) && (details::e_div == operation)) + { + details::free_node(*expr_gen.node_allocator_, branch[0]); + details::free_node(*expr_gen.node_allocator_, branch[1]); + + return expr_gen(T(0)); + } + else if (std::equal_to()(T(0),c) && (details::e_add == operation)) + { + details::free_node(*expr_gen.node_allocator_, branch[0]); + + return branch[1]; + } + else if (std::equal_to()(T(1),c) && (details::e_mul == operation)) + { + details::free_node(*expr_gen.node_allocator_, branch[0]); + + return branch[1]; + } + + if (details::e_add == cobnode->operation()) + { + if (details::e_add == operation) + { + cobnode->set_c(c + cobnode->c()); + result = cobnode; + } + else if (details::e_sub == operation) + { + result = expr_gen.node_allocator_-> + template allocate_tt > > + (c - cobnode->c(), cobnode->move_branch(0)); + + details::free_node(*expr_gen.node_allocator_,branch[1]); + } + } + else if (details::e_sub == cobnode->operation()) + { + if (details::e_add == operation) + { + cobnode->set_c(c + cobnode->c()); + result = cobnode; + } + else if (details::e_sub == operation) + { + result = expr_gen.node_allocator_-> + template allocate_tt > > + (c - cobnode->c(), cobnode->move_branch(0)); + + details::free_node(*expr_gen.node_allocator_,branch[1]); + } + } + else if (details::e_mul == cobnode->operation()) + { + if (details::e_mul == operation) + { + cobnode->set_c(c * cobnode->c()); + result = cobnode; + } + else if (details::e_div == operation) + { + result = expr_gen.node_allocator_-> + template allocate_tt > > + (c / cobnode->c(), cobnode->move_branch(0)); + + details::free_node(*expr_gen.node_allocator_,branch[1]); + } + } + else if (details::e_div == cobnode->operation()) + { + if (details::e_mul == operation) + { + cobnode->set_c(c * cobnode->c()); + result = cobnode; + } + else if (details::e_div == operation) + { + result = expr_gen.node_allocator_-> + template allocate_tt > > + (c / cobnode->c(), cobnode->move_branch(0)); + + details::free_node(*expr_gen.node_allocator_,branch[1]); + } + } + + if (result) + { + details::free_node(*expr_gen.node_allocator_,branch[0]); + } + } + + return result; + } + }; + + struct synthesize_coboc_expression + { + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + expression_node_ptr result = error_node(); + + // (boc) o c --> boc + if (details::is_boc_node(branch[0])) + { + details::boc_base_node* bocnode = static_cast*>(branch[0]); + + const Type c = static_cast*>(branch[1])->value(); + + if (details::e_add == bocnode->operation()) + { + switch (operation) + { + case details::e_add : bocnode->set_c(bocnode->c() + c); break; + case details::e_sub : bocnode->set_c(bocnode->c() - c); break; + default : return error_node(); + } + + result = bocnode; + } + else if (details::e_mul == bocnode->operation()) + { + switch (operation) + { + case details::e_mul : bocnode->set_c(bocnode->c() * c); break; + case details::e_div : bocnode->set_c(bocnode->c() / c); break; + default : return error_node(); + } + + result = bocnode; + } + else if (details::e_sub == bocnode->operation()) + { + if (details::e_add == operation) + { + result = expr_gen.node_allocator_-> + template allocate_tt > > + (bocnode->move_branch(0), c - bocnode->c()); + + details::free_node(*expr_gen.node_allocator_,branch[0]); + } + else if (details::e_sub == operation) + { + bocnode->set_c(bocnode->c() + c); + result = bocnode; + } + } + else if (details::e_div == bocnode->operation()) + { + switch (operation) + { + case details::e_div : bocnode->set_c(bocnode->c() * c); break; + case details::e_mul : bocnode->set_c(bocnode->c() / c); break; + default : return error_node(); + } + + result = bocnode; + } + + if (result) + { + details::free_node(*expr_gen.node_allocator_, branch[1]); + } + } + + // c o (boc) --> boc + else if (details::is_boc_node(branch[1])) + { + details::boc_base_node* bocnode = static_cast*>(branch[1]); + + const Type c = static_cast*>(branch[0])->value(); + + if (details::e_add == bocnode->operation()) + { + if (details::e_add == operation) + { + bocnode->set_c(c + bocnode->c()); + result = bocnode; + } + else if (details::e_sub == operation) + { + result = expr_gen.node_allocator_-> + template allocate_tt > > + (c - bocnode->c(), bocnode->move_branch(0)); + + details::free_node(*expr_gen.node_allocator_,branch[1]); + } + } + else if (details::e_sub == bocnode->operation()) + { + if (details::e_add == operation) + { + result = expr_gen.node_allocator_-> + template allocate_tt > > + (bocnode->move_branch(0), c - bocnode->c()); + + details::free_node(*expr_gen.node_allocator_,branch[1]); + } + else if (details::e_sub == operation) + { + result = expr_gen.node_allocator_-> + template allocate_tt > > + (c + bocnode->c(), bocnode->move_branch(0)); + + details::free_node(*expr_gen.node_allocator_,branch[1]); + } + } + else if (details::e_mul == bocnode->operation()) + { + if (details::e_mul == operation) + { + bocnode->set_c(c * bocnode->c()); + result = bocnode; + } + else if (details::e_div == operation) + { + result = expr_gen.node_allocator_-> + template allocate_tt > > + (c / bocnode->c(), bocnode->move_branch(0)); + + details::free_node(*expr_gen.node_allocator_,branch[1]); + } + } + else if (details::e_div == bocnode->operation()) + { + if (details::e_mul == operation) + { + bocnode->set_c(bocnode->c() / c); + result = bocnode; + } + else if (details::e_div == operation) + { + result = expr_gen.node_allocator_-> + template allocate_tt > > + (c * bocnode->c(), bocnode->move_branch(0)); + + details::free_node(*expr_gen.node_allocator_,branch[1]); + } + } + + if (result) + { + details::free_node(*expr_gen.node_allocator_,branch[0]); + } + } + + return result; + } + }; + + #ifndef exprtk_disable_enhanced_features + inline bool synthesize_expression(const details::operator_type& operation, + expression_node_ptr (&branch)[2], + expression_node_ptr& result) + { + result = error_node(); + + if (!operation_optimisable(operation)) + return false; + + const std::string node_id = branch_to_id(branch); + + const typename synthesize_map_t::iterator itr = synthesize_map_.find(node_id); + + if (synthesize_map_.end() != itr) + { + result = itr->second((*this), operation, branch); + + return true; + } + else + return false; + } + + struct synthesize_vov_expression + { + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + const Type& v1 = static_cast*>(branch[0])->ref(); + const Type& v2 = static_cast*>(branch[1])->ref(); + + switch (operation) + { + #define case_stmt(op0, op1) \ + case op0 : return expr_gen.node_allocator_-> \ + template allocate_rr > > \ + (v1, v2); \ + + basic_opr_switch_statements + extended_opr_switch_statements + #undef case_stmt + default : return error_node(); + } + } + }; + + struct synthesize_cov_expression + { + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + const Type c = static_cast*> (branch[0])->value(); + const Type& v = static_cast*>(branch[1])->ref (); + + details::free_node(*(expr_gen.node_allocator_),branch[0]); + + if (std::equal_to()(T(0),c) && (details::e_mul == operation)) + return expr_gen(T(0)); + else if (std::equal_to()(T(0),c) && (details::e_div == operation)) + return expr_gen(T(0)); + else if (std::equal_to()(T(0),c) && (details::e_add == operation)) + return static_cast*>(branch[1]); + else if (std::equal_to()(T(1),c) && (details::e_mul == operation)) + return static_cast*>(branch[1]); + + switch (operation) + { + #define case_stmt(op0, op1) \ + case op0 : return expr_gen.node_allocator_-> \ + template allocate_cr > > \ + (c, v); \ + + basic_opr_switch_statements + extended_opr_switch_statements + #undef case_stmt + default : return error_node(); + } + } + }; + + struct synthesize_voc_expression + { + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + const Type& v = static_cast*>(branch[0])->ref (); + const Type c = static_cast*> (branch[1])->value(); + + details::free_node(*(expr_gen.node_allocator_), branch[1]); + + if (expr_gen.cardinal_pow_optimisable(operation,c)) + { + if (std::equal_to()(T(1),c)) + return branch[0]; + else + return expr_gen.cardinal_pow_optimisation(v,c); + } + else if (std::equal_to()(T(0),c) && (details::e_mul == operation)) + return expr_gen(T(0)); + else if (std::equal_to()(T(0),c) && (details::e_div == operation)) + return expr_gen(std::numeric_limits::quiet_NaN()); + else if (std::equal_to()(T(0),c) && (details::e_add == operation)) + return static_cast*>(branch[0]); + else if (std::equal_to()(T(1),c) && (details::e_mul == operation)) + return static_cast*>(branch[0]); + else if (std::equal_to()(T(1),c) && (details::e_div == operation)) + return static_cast*>(branch[0]); + + switch (operation) + { + #define case_stmt(op0, op1) \ + case op0 : return expr_gen.node_allocator_-> \ + template allocate_rc > > \ + (v, c); \ + + basic_opr_switch_statements + extended_opr_switch_statements + #undef case_stmt + default : return error_node(); + } + } + }; + + struct synthesize_sf3ext_expression + { + template + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& sf3opr, + T0 t0, T1 t1, T2 t2) + { + switch (sf3opr) + { + #define case_stmt(op) \ + case details::e_sf##op : return details::T0oT1oT2_sf3ext >:: \ + allocate(*(expr_gen.node_allocator_), t0, t1, t2); \ + + case_stmt(00) case_stmt(01) case_stmt(02) case_stmt(03) + case_stmt(04) case_stmt(05) case_stmt(06) case_stmt(07) + case_stmt(08) case_stmt(09) case_stmt(10) case_stmt(11) + case_stmt(12) case_stmt(13) case_stmt(14) case_stmt(15) + case_stmt(16) case_stmt(17) case_stmt(18) case_stmt(19) + case_stmt(20) case_stmt(21) case_stmt(22) case_stmt(23) + case_stmt(24) case_stmt(25) case_stmt(26) case_stmt(27) + case_stmt(28) case_stmt(29) case_stmt(30) + #undef case_stmt + default : return error_node(); + } + } + + template + static inline bool compile(expression_generator& expr_gen, const std::string& id, + T0 t0, T1 t1, T2 t2, + expression_node_ptr& result) + { + details::operator_type sf3opr; + + if (!expr_gen.sf3_optimisable(id,sf3opr)) + return false; + else + result = synthesize_sf3ext_expression::template process + (expr_gen, sf3opr, t0, t1, t2); + + return true; + } + }; + + struct synthesize_sf4ext_expression + { + template + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& sf4opr, + T0 t0, T1 t1, T2 t2, T3 t3) + { + switch (sf4opr) + { + #define case_stmt0(op) \ + case details::e_sf##op : return details::T0oT1oT2oT3_sf4ext >:: \ + allocate(*(expr_gen.node_allocator_), t0, t1, t2, t3); \ + + #define case_stmt1(op) \ + case details::e_sf4ext##op : return details::T0oT1oT2oT3_sf4ext >:: \ + allocate(*(expr_gen.node_allocator_), t0, t1, t2, t3); \ + + case_stmt0(48) case_stmt0(49) case_stmt0(50) case_stmt0(51) + case_stmt0(52) case_stmt0(53) case_stmt0(54) case_stmt0(55) + case_stmt0(56) case_stmt0(57) case_stmt0(58) case_stmt0(59) + case_stmt0(60) case_stmt0(61) case_stmt0(62) case_stmt0(63) + case_stmt0(64) case_stmt0(65) case_stmt0(66) case_stmt0(67) + case_stmt0(68) case_stmt0(69) case_stmt0(70) case_stmt0(71) + case_stmt0(72) case_stmt0(73) case_stmt0(74) case_stmt0(75) + case_stmt0(76) case_stmt0(77) case_stmt0(78) case_stmt0(79) + case_stmt0(80) case_stmt0(81) case_stmt0(82) case_stmt0(83) + + case_stmt1(00) case_stmt1(01) case_stmt1(02) case_stmt1(03) + case_stmt1(04) case_stmt1(05) case_stmt1(06) case_stmt1(07) + case_stmt1(08) case_stmt1(09) case_stmt1(10) case_stmt1(11) + case_stmt1(12) case_stmt1(13) case_stmt1(14) case_stmt1(15) + case_stmt1(16) case_stmt1(17) case_stmt1(18) case_stmt1(19) + case_stmt1(20) case_stmt1(21) case_stmt1(22) case_stmt1(23) + case_stmt1(24) case_stmt1(25) case_stmt1(26) case_stmt1(27) + case_stmt1(28) case_stmt1(29) case_stmt1(30) case_stmt1(31) + case_stmt1(32) case_stmt1(33) case_stmt1(34) case_stmt1(35) + case_stmt1(36) case_stmt1(37) case_stmt1(38) case_stmt1(39) + case_stmt1(40) case_stmt1(41) case_stmt1(42) case_stmt1(43) + case_stmt1(44) case_stmt1(45) case_stmt1(46) case_stmt1(47) + case_stmt1(48) case_stmt1(49) case_stmt1(50) case_stmt1(51) + case_stmt1(52) case_stmt1(53) case_stmt1(54) case_stmt1(55) + case_stmt1(56) case_stmt1(57) case_stmt1(58) case_stmt1(59) + case_stmt1(60) case_stmt1(61) + + #undef case_stmt0 + #undef case_stmt1 + default : return error_node(); + } + } + + template + static inline bool compile(expression_generator& expr_gen, const std::string& id, + T0 t0, T1 t1, T2 t2, T3 t3, + expression_node_ptr& result) + { + details::operator_type sf4opr; + + if (!expr_gen.sf4_optimisable(id,sf4opr)) + return false; + else + result = synthesize_sf4ext_expression::template process + (expr_gen, sf4opr, t0, t1, t2, t3); + + return true; + } + + // T o (sf3ext) + template + static inline bool compile_right(expression_generator& expr_gen, + ExternalType t, + const details::operator_type& operation, + expression_node_ptr& sf3node, + expression_node_ptr& result) + { + if (!details::is_sf3ext_node(sf3node)) + return false; + + typedef details::T0oT1oT2_base_node* sf3ext_base_ptr; + + sf3ext_base_ptr n = static_cast(sf3node); + const std::string id = "t" + expr_gen.to_str(operation) + "(" + n->type_id() + ")"; + + switch (n->type()) + { + case details::expression_node::e_covoc : return compile_right_impl + + (expr_gen, id, t, sf3node, result); + + case details::expression_node::e_covov : return compile_right_impl + + (expr_gen, id, t, sf3node, result); + + case details::expression_node::e_vocov : return compile_right_impl + + (expr_gen, id, t, sf3node, result); + + case details::expression_node::e_vovoc : return compile_right_impl + + (expr_gen, id, t, sf3node, result); + + case details::expression_node::e_vovov : return compile_right_impl + + (expr_gen, id, t, sf3node, result); + + default : return false; + } + } + + // (sf3ext) o T + template + static inline bool compile_left(expression_generator& expr_gen, + ExternalType t, + const details::operator_type& operation, + expression_node_ptr& sf3node, + expression_node_ptr& result) + { + if (!details::is_sf3ext_node(sf3node)) + return false; + + typedef details::T0oT1oT2_base_node* sf3ext_base_ptr; + + sf3ext_base_ptr n = static_cast(sf3node); + + const std::string id = "(" + n->type_id() + ")" + expr_gen.to_str(operation) + "t"; + + switch (n->type()) + { + case details::expression_node::e_covoc : return compile_left_impl + + (expr_gen, id, t, sf3node, result); + + case details::expression_node::e_covov : return compile_left_impl + + (expr_gen, id, t, sf3node, result); + + case details::expression_node::e_vocov : return compile_left_impl + + (expr_gen, id, t, sf3node, result); + + case details::expression_node::e_vovoc : return compile_left_impl + + (expr_gen, id, t, sf3node, result); + + case details::expression_node::e_vovov : return compile_left_impl + + (expr_gen, id, t, sf3node, result); + + default : return false; + } + } + + template + static inline bool compile_right_impl(expression_generator& expr_gen, + const std::string& id, + ExternalType t, + expression_node_ptr& node, + expression_node_ptr& result) + { + SF3TypeNode* n = dynamic_cast(node); + + if (n) + { + T0 t0 = n->t0(); + T1 t1 = n->t1(); + T2 t2 = n->t2(); + + return synthesize_sf4ext_expression::template compile + (expr_gen, id, t, t0, t1, t2, result); + } + else + return false; + } + + template + static inline bool compile_left_impl(expression_generator& expr_gen, + const std::string& id, + ExternalType t, + expression_node_ptr& node, + expression_node_ptr& result) + { + SF3TypeNode* n = dynamic_cast(node); + + if (n) + { + T0 t0 = n->t0(); + T1 t1 = n->t1(); + T2 t2 = n->t2(); + + return synthesize_sf4ext_expression::template compile + (expr_gen, id, t0, t1, t2, t, result); + } + else + return false; + } + }; + + struct synthesize_vovov_expression0 + { + typedef typename vovov_t::type0 node_type; + typedef typename vovov_t::sf3_type sf3_type; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // (v0 o0 v1) o1 (v2) + const details::vov_base_node* vov = static_cast*>(branch[0]); + const Type& v0 = vov->v0(); + const Type& v1 = vov->v1(); + const Type& v2 = static_cast*>(branch[1])->ref(); + const details::operator_type o0 = vov->operation(); + const details::operator_type o1 = operation; + + details::free_node(*(expr_gen.node_allocator_),branch[0]); + + expression_node_ptr result = error_node(); + + if (expr_gen.parser_->settings_.strength_reduction_enabled()) + { + // (v0 / v1) / v2 --> (vovov) v0 / (v1 * v2) + if ((details::e_div == o0) && (details::e_div == o1)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "t/(t*t)", v0, v1, v2, result); + + exprtk_debug(("(v0 / v1) / v2 --> (vovov) v0 / (v1 * v2)\n")); + + return (synthesis_result) ? result : error_node(); + } + } + + const bool synthesis_result = + synthesize_sf3ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1), v0, v1, v2, result); + + if (synthesis_result) + return result; + + binary_functor_t f0 = reinterpret_cast(0); + binary_functor_t f1 = reinterpret_cast(0); + + if (!expr_gen.valid_operator(o0,f0)) + return error_node(); + else if (!expr_gen.valid_operator(o1,f1)) + return error_node(); + else + return node_type::allocate(*(expr_gen.node_allocator_), v0, v1, v2, f0, f1); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1) + { + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "t"; + } + }; + + struct synthesize_vovov_expression1 + { + typedef typename vovov_t::type1 node_type; + typedef typename vovov_t::sf3_type sf3_type; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // (v0) o0 (v1 o1 v2) + const details::vov_base_node* vov = static_cast*>(branch[1]); + const Type& v0 = static_cast*>(branch[0])->ref(); + const Type& v1 = vov->v0(); + const Type& v2 = vov->v1(); + const details::operator_type o0 = operation; + const details::operator_type o1 = vov->operation(); + + details::free_node(*(expr_gen.node_allocator_),branch[1]); + + expression_node_ptr result = error_node(); + + if (expr_gen.parser_->settings_.strength_reduction_enabled()) + { + // v0 / (v1 / v2) --> (vovov) (v0 * v2) / v1 + if ((details::e_div == o0) && (details::e_div == o1)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "(t*t)/t", v0, v2, v1, result); + + exprtk_debug(("v0 / (v1 / v2) --> (vovov) (v0 * v2) / v1\n")); + + return (synthesis_result) ? result : error_node(); + } + } + + const bool synthesis_result = + synthesize_sf3ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1), v0, v1, v2, result); + + if (synthesis_result) + return result; + + binary_functor_t f0 = reinterpret_cast(0); + binary_functor_t f1 = reinterpret_cast(0); + + if (!expr_gen.valid_operator(o0,f0)) + return error_node(); + else if (!expr_gen.valid_operator(o1,f1)) + return error_node(); + else + return node_type::allocate(*(expr_gen.node_allocator_), v0, v1, v2, f0, f1); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1) + { + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "t)"; + } + }; + + struct synthesize_vovoc_expression0 + { + typedef typename vovoc_t::type0 node_type; + typedef typename vovoc_t::sf3_type sf3_type; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // (v0 o0 v1) o1 (c) + const details::vov_base_node* vov = static_cast*>(branch[0]); + const Type& v0 = vov->v0(); + const Type& v1 = vov->v1(); + const Type c = static_cast*>(branch[1])->value(); + const details::operator_type o0 = vov->operation(); + const details::operator_type o1 = operation; + + details::free_node(*(expr_gen.node_allocator_),branch[0]); + details::free_node(*(expr_gen.node_allocator_),branch[1]); + + expression_node_ptr result = error_node(); + + if (expr_gen.parser_->settings_.strength_reduction_enabled()) + { + // (v0 / v1) / c --> (vovoc) v0 / (v1 * c) + if ((details::e_div == o0) && (details::e_div == o1)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "t/(t*t)", v0, v1, c, result); + + exprtk_debug(("(v0 / v1) / c --> (vovoc) v0 / (v1 * c)\n")); + + return (synthesis_result) ? result : error_node(); + } + } + + const bool synthesis_result = + synthesize_sf3ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1), v0, v1, c, result); + + if (synthesis_result) + return result; + + binary_functor_t f0 = reinterpret_cast(0); + binary_functor_t f1 = reinterpret_cast(0); + + if (!expr_gen.valid_operator(o0,f0)) + return error_node(); + else if (!expr_gen.valid_operator(o1,f1)) + return error_node(); + else + return node_type::allocate(*(expr_gen.node_allocator_), v0, v1, c, f0, f1); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1) + { + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "t"; + } + }; + + struct synthesize_vovoc_expression1 + { + typedef typename vovoc_t::type1 node_type; + typedef typename vovoc_t::sf3_type sf3_type; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // (v0) o0 (v1 o1 c) + const details::voc_base_node* voc = static_cast*>(branch[1]); + const Type& v0 = static_cast*>(branch[0])->ref(); + const Type& v1 = voc->v(); + const Type c = voc->c(); + const details::operator_type o0 = operation; + const details::operator_type o1 = voc->operation(); + + details::free_node(*(expr_gen.node_allocator_),branch[1]); + + expression_node_ptr result = error_node(); + + if (expr_gen.parser_->settings_.strength_reduction_enabled()) + { + // v0 / (v1 / c) --> (vocov) (v0 * c) / v1 + if ((details::e_div == o0) && (details::e_div == o1)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "(t*t)/t", v0, c, v1, result); + + exprtk_debug(("v0 / (v1 / c) --> (vocov) (v0 * c) / v1\n")); + + return (synthesis_result) ? result : error_node(); + } + } + + const bool synthesis_result = + synthesize_sf3ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1), v0, v1, c, result); + + if (synthesis_result) + return result; + + binary_functor_t f0 = reinterpret_cast(0); + binary_functor_t f1 = reinterpret_cast(0); + + if (!expr_gen.valid_operator(o0,f0)) + return error_node(); + else if (!expr_gen.valid_operator(o1,f1)) + return error_node(); + else + return node_type::allocate(*(expr_gen.node_allocator_), v0, v1, c, f0, f1); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1) + { + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "t)"; + } + }; + + struct synthesize_vocov_expression0 + { + typedef typename vocov_t::type0 node_type; + typedef typename vocov_t::sf3_type sf3_type; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // (v0 o0 c) o1 (v1) + const details::voc_base_node* voc = static_cast*>(branch[0]); + const Type& v0 = voc->v(); + const Type c = voc->c(); + const Type& v1 = static_cast*>(branch[1])->ref(); + const details::operator_type o0 = voc->operation(); + const details::operator_type o1 = operation; + + details::free_node(*(expr_gen.node_allocator_),branch[0]); + + expression_node_ptr result = error_node(); + + if (expr_gen.parser_->settings_.strength_reduction_enabled()) + { + // (v0 / c) / v1 --> (vovoc) v0 / (v1 * c) + if ((details::e_div == o0) && (details::e_div == o1)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "t/(t*t)", v0, v1, c, result); + + exprtk_debug(("(v0 / c) / v1 --> (vovoc) v0 / (v1 * c)\n")); + + return (synthesis_result) ? result : error_node(); + } + } + + const bool synthesis_result = + synthesize_sf3ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1), v0, c, v1, result); + + if (synthesis_result) + return result; + + binary_functor_t f0 = reinterpret_cast(0); + binary_functor_t f1 = reinterpret_cast(0); + + if (!expr_gen.valid_operator(o0,f0)) + return error_node(); + else if (!expr_gen.valid_operator(o1,f1)) + return error_node(); + else + return node_type::allocate(*(expr_gen.node_allocator_), v0, c, v1, f0, f1); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1) + { + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "t"; + } + }; + + struct synthesize_vocov_expression1 + { + typedef typename vocov_t::type1 node_type; + typedef typename vocov_t::sf3_type sf3_type; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // (v0) o0 (c o1 v1) + const details::cov_base_node* cov = static_cast*>(branch[1]); + const Type& v0 = static_cast*>(branch[0])->ref(); + const Type c = cov->c(); + const Type& v1 = cov->v(); + const details::operator_type o0 = operation; + const details::operator_type o1 = cov->operation(); + + details::free_node(*(expr_gen.node_allocator_),branch[1]); + + expression_node_ptr result = error_node(); + + if (expr_gen.parser_->settings_.strength_reduction_enabled()) + { + // v0 / (c / v1) --> (vovoc) (v0 * v1) / c + if ((details::e_div == o0) && (details::e_div == o1)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "(t*t)/t", v0, v1, c, result); + + exprtk_debug(("v0 / (c / v1) --> (vovoc) (v0 * v1) / c\n")); + + return (synthesis_result) ? result : error_node(); + } + } + + const bool synthesis_result = + synthesize_sf3ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1), v0, c, v1, result); + + if (synthesis_result) + return result; + + binary_functor_t f0 = reinterpret_cast(0); + binary_functor_t f1 = reinterpret_cast(0); + + if (!expr_gen.valid_operator(o0,f0)) + return error_node(); + else if (!expr_gen.valid_operator(o1,f1)) + return error_node(); + else + return node_type::allocate(*(expr_gen.node_allocator_), v0, c, v1, f0, f1); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1) + { + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "t)"; + } + }; + + struct synthesize_covov_expression0 + { + typedef typename covov_t::type0 node_type; + typedef typename covov_t::sf3_type sf3_type; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // (c o0 v0) o1 (v1) + const details::cov_base_node* cov = static_cast*>(branch[0]); + const Type c = cov->c(); + const Type& v0 = cov->v(); + const Type& v1 = static_cast*>(branch[1])->ref(); + const details::operator_type o0 = cov->operation(); + const details::operator_type o1 = operation; + + details::free_node(*(expr_gen.node_allocator_),branch[0]); + + expression_node_ptr result = error_node(); + + if (expr_gen.parser_->settings_.strength_reduction_enabled()) + { + // (c / v0) / v1 --> (covov) c / (v0 * v1) + if ((details::e_div == o0) && (details::e_div == o1)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "t/(t*t)", c, v0, v1, result); + + exprtk_debug(("(c / v0) / v1 --> (covov) c / (v0 * v1)\n")); + + return (synthesis_result) ? result : error_node(); + } + } + + const bool synthesis_result = + synthesize_sf3ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1), c, v0, v1, result); + + if (synthesis_result) + return result; + + binary_functor_t f0 = reinterpret_cast(0); + binary_functor_t f1 = reinterpret_cast(0); + + if (!expr_gen.valid_operator(o0,f0)) + return error_node(); + else if (!expr_gen.valid_operator(o1,f1)) + return error_node(); + else + return node_type::allocate(*(expr_gen.node_allocator_), c, v0, v1, f0, f1); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1) + { + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "t"; + } + }; + + struct synthesize_covov_expression1 + { + typedef typename covov_t::type1 node_type; + typedef typename covov_t::sf3_type sf3_type; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // (c) o0 (v0 o1 v1) + const details::vov_base_node* vov = static_cast*>(branch[1]); + const Type c = static_cast*>(branch[0])->value(); + const Type& v0 = vov->v0(); + const Type& v1 = vov->v1(); + const details::operator_type o0 = operation; + const details::operator_type o1 = vov->operation(); + + details::free_node(*(expr_gen.node_allocator_),branch[0]); + details::free_node(*(expr_gen.node_allocator_),branch[1]); + + expression_node_ptr result = error_node(); + + if (expr_gen.parser_->settings_.strength_reduction_enabled()) + { + // c / (v0 / v1) --> (covov) (c * v1) / v0 + if ((details::e_div == o0) && (details::e_div == o1)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "(t*t)/t", c, v1, v0, result); + + exprtk_debug(("c / (v0 / v1) --> (covov) (c * v1) / v0\n")); + + return (synthesis_result) ? result : error_node(); + } + } + + const bool synthesis_result = + synthesize_sf3ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1), c, v0, v1, result); + + if (synthesis_result) + return result; + + binary_functor_t f0 = reinterpret_cast(0); + binary_functor_t f1 = reinterpret_cast(0); + + if (!expr_gen.valid_operator(o0,f0)) + return error_node(); + else if (!expr_gen.valid_operator(o1,f1)) + return error_node(); + else + return node_type::allocate(*(expr_gen.node_allocator_), c, v0, v1, f0, f1); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1) + { + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "t)"; + } + }; + + struct synthesize_covoc_expression0 + { + typedef typename covoc_t::type0 node_type; + typedef typename covoc_t::sf3_type sf3_type; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // (c0 o0 v) o1 (c1) + const details::cov_base_node* cov = static_cast*>(branch[0]); + const Type c0 = cov->c(); + const Type& v = cov->v(); + const Type c1 = static_cast*>(branch[1])->value(); + const details::operator_type o0 = cov->operation(); + const details::operator_type o1 = operation; + + details::free_node(*(expr_gen.node_allocator_),branch[0]); + details::free_node(*(expr_gen.node_allocator_),branch[1]); + + expression_node_ptr result = error_node(); + + if (expr_gen.parser_->settings_.strength_reduction_enabled()) + { + // (c0 + v) + c1 --> (cov) (c0 + c1) + v + if ((details::e_add == o0) && (details::e_add == o1)) + { + exprtk_debug(("(c0 + v) + c1 --> (cov) (c0 + c1) + v\n")); + + return expr_gen.node_allocator_-> + template allocate_cr > >(c0 + c1, v); + } + // (c0 + v) - c1 --> (cov) (c0 - c1) + v + else if ((details::e_add == o0) && (details::e_sub == o1)) + { + exprtk_debug(("(c0 + v) - c1 --> (cov) (c0 - c1) + v\n")); + + return expr_gen.node_allocator_-> + template allocate_cr > >(c0 - c1, v); + } + // (c0 - v) + c1 --> (cov) (c0 + c1) - v + else if ((details::e_sub == o0) && (details::e_add == o1)) + { + exprtk_debug(("(c0 - v) + c1 --> (cov) (c0 + c1) - v\n")); + + return expr_gen.node_allocator_-> + template allocate_cr > >(c0 + c1, v); + } + // (c0 - v) - c1 --> (cov) (c0 - c1) - v + else if ((details::e_sub == o0) && (details::e_sub == o1)) + { + exprtk_debug(("(c0 - v) - c1 --> (cov) (c0 - c1) - v\n")); + + return expr_gen.node_allocator_-> + template allocate_cr > >(c0 - c1, v); + } + // (c0 * v) * c1 --> (cov) (c0 * c1) * v + else if ((details::e_mul == o0) && (details::e_mul == o1)) + { + exprtk_debug(("(c0 * v) * c1 --> (cov) (c0 * c1) * v\n")); + + return expr_gen.node_allocator_-> + template allocate_cr > >(c0 * c1, v); + } + // (c0 * v) / c1 --> (cov) (c0 / c1) * v + else if ((details::e_mul == o0) && (details::e_div == o1)) + { + exprtk_debug(("(c0 * v) / c1 --> (cov) (c0 / c1) * v\n")); + + return expr_gen.node_allocator_-> + template allocate_cr > >(c0 / c1, v); + } + // (c0 / v) * c1 --> (cov) (c0 * c1) / v + else if ((details::e_div == o0) && (details::e_mul == o1)) + { + exprtk_debug(("(c0 / v) * c1 --> (cov) (c0 * c1) / v\n")); + + return expr_gen.node_allocator_-> + template allocate_cr > >(c0 * c1, v); + } + // (c0 / v) / c1 --> (cov) (c0 / c1) / v + else if ((details::e_div == o0) && (details::e_div == o1)) + { + exprtk_debug(("(c0 / v) / c1 --> (cov) (c0 / c1) / v\n")); + + return expr_gen.node_allocator_-> + template allocate_cr > >(c0 / c1, v); + } + } + + const bool synthesis_result = + synthesize_sf3ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1), c0, v, c1, result); + + if (synthesis_result) + return result; + + binary_functor_t f0 = reinterpret_cast(0); + binary_functor_t f1 = reinterpret_cast(0); + + if (!expr_gen.valid_operator(o0,f0)) + return error_node(); + else if (!expr_gen.valid_operator(o1,f1)) + return error_node(); + else + return node_type::allocate(*(expr_gen.node_allocator_), c0, v, c1, f0, f1); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1) + { + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "t"; + } + }; + + struct synthesize_covoc_expression1 + { + typedef typename covoc_t::type1 node_type; + typedef typename covoc_t::sf3_type sf3_type; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // (c0) o0 (v o1 c1) + const details::voc_base_node* voc = static_cast*>(branch[1]); + const Type c0 = static_cast*>(branch[0])->value(); + const Type& v = voc->v(); + const Type c1 = voc->c(); + const details::operator_type o0 = operation; + const details::operator_type o1 = voc->operation(); + + details::free_node(*(expr_gen.node_allocator_),branch[0]); + details::free_node(*(expr_gen.node_allocator_),branch[1]); + + expression_node_ptr result = error_node(); + + if (expr_gen.parser_->settings_.strength_reduction_enabled()) + { + // (c0) + (v + c1) --> (cov) (c0 + c1) + v + if ((details::e_add == o0) && (details::e_add == o1)) + { + exprtk_debug(("(c0) + (v + c1) --> (cov) (c0 + c1) + v\n")); + + return expr_gen.node_allocator_-> + template allocate_cr > >(c0 + c1, v); + } + // (c0) + (v - c1) --> (cov) (c0 - c1) + v + else if ((details::e_add == o0) && (details::e_sub == o1)) + { + exprtk_debug(("(c0) + (v - c1) --> (cov) (c0 - c1) + v\n")); + + return expr_gen.node_allocator_-> + template allocate_cr > >(c0 - c1, v); + } + // (c0) - (v + c1) --> (cov) (c0 - c1) - v + else if ((details::e_sub == o0) && (details::e_add == o1)) + { + exprtk_debug(("(c0) - (v + c1) --> (cov) (c0 - c1) - v\n")); + + return expr_gen.node_allocator_-> + template allocate_cr > >(c0 - c1, v); + } + // (c0) - (v - c1) --> (cov) (c0 + c1) - v + else if ((details::e_sub == o0) && (details::e_sub == o1)) + { + exprtk_debug(("(c0) - (v - c1) --> (cov) (c0 + c1) - v\n")); + + return expr_gen.node_allocator_-> + template allocate_cr > >(c0 + c1, v); + } + // (c0) * (v * c1) --> (voc) v * (c0 * c1) + else if ((details::e_mul == o0) && (details::e_mul == o1)) + { + exprtk_debug(("(c0) * (v * c1) --> (voc) v * (c0 * c1)\n")); + + return expr_gen.node_allocator_-> + template allocate_cr > >(c0 * c1, v); + } + // (c0) * (v / c1) --> (cov) (c0 / c1) * v + else if ((details::e_mul == o0) && (details::e_div == o1)) + { + exprtk_debug(("(c0) * (v / c1) --> (cov) (c0 / c1) * v\n")); + + return expr_gen.node_allocator_-> + template allocate_cr > >(c0 / c1, v); + } + // (c0) / (v * c1) --> (cov) (c0 / c1) / v + else if ((details::e_div == o0) && (details::e_mul == o1)) + { + exprtk_debug(("(c0) / (v * c1) --> (cov) (c0 / c1) / v\n")); + + return expr_gen.node_allocator_-> + template allocate_cr > >(c0 / c1, v); + } + // (c0) / (v / c1) --> (cov) (c0 * c1) / v + else if ((details::e_div == o0) && (details::e_div == o1)) + { + exprtk_debug(("(c0) / (v / c1) --> (cov) (c0 * c1) / v\n")); + + return expr_gen.node_allocator_-> + template allocate_cr > >(c0 * c1, v); + } + } + + const bool synthesis_result = + synthesize_sf3ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1), c0, v, c1, result); + + if (synthesis_result) + return result; + + binary_functor_t f0 = reinterpret_cast(0); + binary_functor_t f1 = reinterpret_cast(0); + + if (!expr_gen.valid_operator(o0,f0)) + return error_node(); + else if (!expr_gen.valid_operator(o1,f1)) + return error_node(); + else + return node_type::allocate(*(expr_gen.node_allocator_), c0, v, c1, f0, f1); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1) + { + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "t)"; + } + }; + + struct synthesize_cocov_expression0 + { + typedef typename cocov_t::type0 node_type; + static inline expression_node_ptr process(expression_generator&, + const details::operator_type&, + expression_node_ptr (&)[2]) + { + // (c0 o0 c1) o1 (v) - Not possible. + return error_node(); + } + }; + + struct synthesize_cocov_expression1 + { + typedef typename cocov_t::type1 node_type; + typedef typename cocov_t::sf3_type sf3_type; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // (c0) o0 (c1 o1 v) + const details::cov_base_node* cov = static_cast*>(branch[1]); + const Type c0 = static_cast*>(branch[0])->value(); + const Type c1 = cov->c(); + const Type& v = cov->v(); + const details::operator_type o0 = operation; + const details::operator_type o1 = cov->operation(); + + details::free_node(*(expr_gen.node_allocator_),branch[0]); + details::free_node(*(expr_gen.node_allocator_),branch[1]); + + expression_node_ptr result = error_node(); + + if (expr_gen.parser_->settings_.strength_reduction_enabled()) + { + // (c0) + (c1 + v) --> (cov) (c0 + c1) + v + if ((details::e_add == o0) && (details::e_add == o1)) + { + exprtk_debug(("(c0) + (c1 + v) --> (cov) (c0 + c1) + v\n")); + + return expr_gen.node_allocator_-> + template allocate_cr > >(c0 + c1, v); + } + // (c0) + (c1 - v) --> (cov) (c0 + c1) - v + else if ((details::e_add == o0) && (details::e_sub == o1)) + { + exprtk_debug(("(c0) + (c1 - v) --> (cov) (c0 + c1) - v\n")); + + return expr_gen.node_allocator_-> + template allocate_cr > >(c0 + c1, v); + } + // (c0) - (c1 + v) --> (cov) (c0 - c1) - v + else if ((details::e_sub == o0) && (details::e_add == o1)) + { + exprtk_debug(("(c0) - (c1 + v) --> (cov) (c0 - c1) - v\n")); + + return expr_gen.node_allocator_-> + template allocate_cr > >(c0 - c1, v); + } + // (c0) - (c1 - v) --> (cov) (c0 - c1) + v + else if ((details::e_sub == o0) && (details::e_sub == o1)) + { + exprtk_debug(("(c0) - (c1 - v) --> (cov) (c0 - c1) + v\n")); + + return expr_gen.node_allocator_-> + template allocate_cr > >(c0 - c1, v); + } + // (c0) * (c1 * v) --> (cov) (c0 * c1) * v + else if ((details::e_mul == o0) && (details::e_mul == o1)) + { + exprtk_debug(("(c0) * (c1 * v) --> (cov) (c0 * c1) * v\n")); + + return expr_gen.node_allocator_-> + template allocate_cr > >(c0 * c1, v); + } + // (c0) * (c1 / v) --> (cov) (c0 * c1) / v + else if ((details::e_mul == o0) && (details::e_div == o1)) + { + exprtk_debug(("(c0) * (c1 / v) --> (cov) (c0 * c1) / v\n")); + + return expr_gen.node_allocator_-> + template allocate_cr > >(c0 * c1, v); + } + // (c0) / (c1 * v) --> (cov) (c0 / c1) / v + else if ((details::e_div == o0) && (details::e_mul == o1)) + { + exprtk_debug(("(c0) / (c1 * v) --> (cov) (c0 / c1) / v\n")); + + return expr_gen.node_allocator_-> + template allocate_cr > >(c0 / c1, v); + } + // (c0) / (c1 / v) --> (cov) (c0 / c1) * v + else if ((details::e_div == o0) && (details::e_div == o1)) + { + exprtk_debug(("(c0) / (c1 / v) --> (cov) (c0 / c1) * v\n")); + + return expr_gen.node_allocator_-> + template allocate_cr > >(c0 / c1, v); + } + } + + const bool synthesis_result = + synthesize_sf3ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1), c0, c1, v, result); + + if (synthesis_result) + return result; + + binary_functor_t f0 = reinterpret_cast(0); + binary_functor_t f1 = reinterpret_cast(0); + + if (!expr_gen.valid_operator(o0,f0)) + return error_node(); + else if (!expr_gen.valid_operator(o1,f1)) + return error_node(); + else + return node_type::allocate(*(expr_gen.node_allocator_), c0, c1, v, f0, f1); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1) + { + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "t)"; + } + }; + + struct synthesize_vococ_expression0 + { + typedef typename vococ_t::type0 node_type; + typedef typename vococ_t::sf3_type sf3_type; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // (v o0 c0) o1 (c1) + const details::voc_base_node* voc = static_cast*>(branch[0]); + const Type& v = voc->v(); + const Type& c0 = voc->c(); + const Type& c1 = static_cast*>(branch[1])->value(); + const details::operator_type o0 = voc->operation(); + const details::operator_type o1 = operation; + + details::free_node(*(expr_gen.node_allocator_),branch[0]); + details::free_node(*(expr_gen.node_allocator_),branch[1]); + + expression_node_ptr result = error_node(); + + if (expr_gen.parser_->settings_.strength_reduction_enabled()) + { + // (v + c0) + c1 --> (voc) v + (c0 + c1) + if ((details::e_add == o0) && (details::e_add == o1)) + { + exprtk_debug(("(v + c0) + c1 --> (voc) v + (c0 + c1)\n")); + + return expr_gen.node_allocator_-> + template allocate_rc > >(v, c0 + c1); + } + // (v + c0) - c1 --> (voc) v + (c0 - c1) + else if ((details::e_add == o0) && (details::e_sub == o1)) + { + exprtk_debug(("(v + c0) - c1 --> (voc) v + (c0 - c1)\n")); + + return expr_gen.node_allocator_-> + template allocate_rc > >(v, c0 - c1); + } + // (v - c0) + c1 --> (voc) v - (c0 + c1) + else if ((details::e_sub == o0) && (details::e_add == o1)) + { + exprtk_debug(("(v - c0) + c1 --> (voc) v - (c0 + c1)\n")); + + return expr_gen.node_allocator_-> + template allocate_rc > >(v, c1 - c0); + } + // (v - c0) - c1 --> (voc) v - (c0 + c1) + else if ((details::e_sub == o0) && (details::e_sub == o1)) + { + exprtk_debug(("(v - c0) - c1 --> (voc) v - (c0 + c1)\n")); + + return expr_gen.node_allocator_-> + template allocate_rc > >(v, c0 + c1); + } + // (v * c0) * c1 --> (voc) v * (c0 * c1) + else if ((details::e_mul == o0) && (details::e_mul == o1)) + { + exprtk_debug(("(v * c0) * c1 --> (voc) v * (c0 * c1)\n")); + + return expr_gen.node_allocator_-> + template allocate_rc > >(v, c0 * c1); + } + // (v * c0) / c1 --> (voc) v * (c0 / c1) + else if ((details::e_mul == o0) && (details::e_div == o1)) + { + exprtk_debug(("(v * c0) / c1 --> (voc) v * (c0 / c1)\n")); + + return expr_gen.node_allocator_-> + template allocate_rc > >(v, c0 / c1); + } + // (v / c0) * c1 --> (voc) v * (c1 / c0) + else if ((details::e_div == o0) && (details::e_mul == o1)) + { + exprtk_debug(("(v / c0) * c1 --> (voc) v * (c1 / c0)\n")); + + return expr_gen.node_allocator_-> + template allocate_rc > >(v, c1 / c0); + } + // (v / c0) / c1 --> (voc) v / (c0 * c1) + else if ((details::e_div == o0) && (details::e_div == o1)) + { + exprtk_debug(("(v / c0) / c1 --> (voc) v / (c0 * c1)\n")); + + return expr_gen.node_allocator_-> + template allocate_rc > >(v, c0 * c1); + } + // (v ^ c0) ^ c1 --> (voc) v ^ (c0 * c1) + else if ((details::e_pow == o0) && (details::e_pow == o1)) + { + exprtk_debug(("(v ^ c0) ^ c1 --> (voc) v ^ (c0 * c1)\n")); + + return expr_gen.node_allocator_-> + template allocate_rc > >(v, c0 * c1); + } + } + + const bool synthesis_result = + synthesize_sf3ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1), v, c0, c1, result); + + if (synthesis_result) + return result; + + binary_functor_t f0 = reinterpret_cast(0); + binary_functor_t f1 = reinterpret_cast(0); + + if (!expr_gen.valid_operator(o0,f0)) + return error_node(); + else if (!expr_gen.valid_operator(o1,f1)) + return error_node(); + else + return node_type::allocate(*(expr_gen.node_allocator_), v, c0, c1, f0, f1); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1) + { + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "t"; + } + }; + + struct synthesize_vococ_expression1 + { + typedef typename vococ_t::type0 node_type; + + static inline expression_node_ptr process(expression_generator&, + const details::operator_type&, + expression_node_ptr (&)[2]) + { + // (v) o0 (c0 o1 c1) - Not possible. + exprtk_debug(("(v) o0 (c0 o1 c1) - Not possible.\n")); + return error_node(); + } + }; + + struct synthesize_vovovov_expression0 + { + typedef typename vovovov_t::type0 node_type; + typedef typename vovovov_t::sf4_type sf4_type; + typedef typename node_type::T0 T0; + typedef typename node_type::T1 T1; + typedef typename node_type::T2 T2; + typedef typename node_type::T3 T3; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // (v0 o0 v1) o1 (v2 o2 v3) + const details::vov_base_node* vov0 = static_cast*>(branch[0]); + const details::vov_base_node* vov1 = static_cast*>(branch[1]); + const Type& v0 = vov0->v0(); + const Type& v1 = vov0->v1(); + const Type& v2 = vov1->v0(); + const Type& v3 = vov1->v1(); + const details::operator_type o0 = vov0->operation(); + const details::operator_type o1 = operation; + const details::operator_type o2 = vov1->operation(); + + details::free_node(*(expr_gen.node_allocator_),branch[0]); + details::free_node(*(expr_gen.node_allocator_),branch[1]); + + expression_node_ptr result = error_node(); + + if (expr_gen.parser_->settings_.strength_reduction_enabled()) + { + // (v0 / v1) * (v2 / v3) --> (vovovov) (v0 * v2) / (v1 * v3) + if ((details::e_div == o0) && (details::e_mul == o1) && (details::e_div == o2)) + { + const bool synthesis_result = + synthesize_sf4ext_expression:: + template compile(expr_gen, "(t*t)/(t*t)", v0, v2, v1, v3, result); + + exprtk_debug(("(v0 / v1) * (v2 / v3) --> (vovovov) (v0 * v2) / (v1 * v3)\n")); + + return (synthesis_result) ? result : error_node(); + } + // (v0 / v1) / (v2 / v3) --> (vovovov) (v0 * v3) / (v1 * v2) + else if ((details::e_div == o0) && (details::e_div == o1) && (details::e_div == o2)) + { + const bool synthesis_result = + synthesize_sf4ext_expression:: + template compile(expr_gen, "(t*t)/(t*t)", v0, v3, v1, v2, result); + + exprtk_debug(("(v0 / v1) / (v2 / v3) --> (vovovov) (v0 * v3) / (v1 * v2)\n")); + + return (synthesis_result) ? result : error_node(); + } + // (v0 + v1) / (v2 / v3) --> (vovovov) (v0 + v1) * (v3 / v2) + else if ((details::e_add == o0) && (details::e_div == o1) && (details::e_div == o2)) + { + const bool synthesis_result = + synthesize_sf4ext_expression:: + template compile(expr_gen, "(t+t)*(t/t)", v0, v1, v3, v2, result); + + exprtk_debug(("(v0 + v1) / (v2 / v3) --> (vovovov) (v0 + v1) * (v3 / v2)\n")); + + return (synthesis_result) ? result : error_node(); + } + // (v0 - v1) / (v2 / v3) --> (vovovov) (v0 + v1) * (v3 / v2) + else if ((details::e_sub == o0) && (details::e_div == o1) && (details::e_div == o2)) + { + const bool synthesis_result = + synthesize_sf4ext_expression:: + template compile(expr_gen, "(t-t)*(t/t)", v0, v1, v3, v2, result); + + exprtk_debug(("(v0 - v1) / (v2 / v3) --> (vovovov) (v0 - v1) * (v3 / v2)\n")); + + return (synthesis_result) ? result : error_node(); + } + // (v0 * v1) / (v2 / v3) --> (vovovov) ((v0 * v1) * v3) / v2 + else if ((details::e_mul == o0) && (details::e_div == o1) && (details::e_div == o2)) + { + const bool synthesis_result = + synthesize_sf4ext_expression:: + template compile(expr_gen, "((t*t)*t)/t", v0, v1, v3, v2, result); + + exprtk_debug(("(v0 * v1) / (v2 / v3) --> (vovovov) ((v0 * v1) * v3) / v2\n")); + + return (synthesis_result) ? result : error_node(); + } + } + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1, o2), v0, v1, v2, v3, result); + + if (synthesis_result) + return result; + + binary_functor_t f0 = reinterpret_cast(0); + binary_functor_t f1 = reinterpret_cast(0); + binary_functor_t f2 = reinterpret_cast(0); + + if (!expr_gen.valid_operator(o0,f0)) + return error_node(); + else if (!expr_gen.valid_operator(o1,f1)) + return error_node(); + else if (!expr_gen.valid_operator(o2,f2)) + return error_node(); + else + return node_type::allocate(*(expr_gen.node_allocator_), v0, v1, v2, v3, f0, f1, f2); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1, + const details::operator_type o2) + { + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "(t" << expr_gen.to_str(o2) + << "t)"; + } + }; + + struct synthesize_vovovoc_expression0 + { + typedef typename vovovoc_t::type0 node_type; + typedef typename vovovoc_t::sf4_type sf4_type; + typedef typename node_type::T0 T0; + typedef typename node_type::T1 T1; + typedef typename node_type::T2 T2; + typedef typename node_type::T3 T3; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // (v0 o0 v1) o1 (v2 o2 c) + const details::vov_base_node* vov = static_cast*>(branch[0]); + const details::voc_base_node* voc = static_cast*>(branch[1]); + const Type& v0 = vov->v0(); + const Type& v1 = vov->v1(); + const Type& v2 = voc->v (); + const Type c = voc->c (); + const details::operator_type o0 = vov->operation(); + const details::operator_type o1 = operation; + const details::operator_type o2 = voc->operation(); + + details::free_node(*(expr_gen.node_allocator_),branch[0]); + details::free_node(*(expr_gen.node_allocator_),branch[1]); + + expression_node_ptr result = error_node(); + + if (expr_gen.parser_->settings_.strength_reduction_enabled()) + { + // (v0 / v1) * (v2 / c) --> (vovovoc) (v0 * v2) / (v1 * c) + if ((details::e_div == o0) && (details::e_mul == o1) && (details::e_div == o2)) + { + const bool synthesis_result = + synthesize_sf4ext_expression:: + template compile(expr_gen, "(t*t)/(t*t)", v0, v2, v1, c, result); + + exprtk_debug(("(v0 / v1) * (v2 / c) --> (vovovoc) (v0 * v2) / (v1 * c)\n")); + + return (synthesis_result) ? result : error_node(); + } + // (v0 / v1) / (v2 / c) --> (vocovov) (v0 * c) / (v1 * v2) + if ((details::e_div == o0) && (details::e_div == o1) && (details::e_div == o2)) + { + const bool synthesis_result = + synthesize_sf4ext_expression:: + template compile(expr_gen, "(t*t)/(t*t)", v0, c, v1, v2, result); + + exprtk_debug(("(v0 / v1) / (v2 / c) --> (vocovov) (v0 * c) / (v1 * v2)\n")); + + return (synthesis_result) ? result : error_node(); + } + } + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1, o2), v0, v1, v2, c, result); + + if (synthesis_result) + return result; + + binary_functor_t f0 = reinterpret_cast(0); + binary_functor_t f1 = reinterpret_cast(0); + binary_functor_t f2 = reinterpret_cast(0); + + if (!expr_gen.valid_operator(o0,f0)) + return error_node(); + else if (!expr_gen.valid_operator(o1,f1)) + return error_node(); + else if (!expr_gen.valid_operator(o2,f2)) + return error_node(); + else + return node_type::allocate(*(expr_gen.node_allocator_), v0, v1, v2, c, f0, f1, f2); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1, + const details::operator_type o2) + { + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "(t" << expr_gen.to_str(o2) + << "t)"; + } + }; + + struct synthesize_vovocov_expression0 + { + typedef typename vovocov_t::type0 node_type; + typedef typename vovocov_t::sf4_type sf4_type; + typedef typename node_type::T0 T0; + typedef typename node_type::T1 T1; + typedef typename node_type::T2 T2; + typedef typename node_type::T3 T3; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // (v0 o0 v1) o1 (c o2 v2) + const details::vov_base_node* vov = static_cast*>(branch[0]); + const details::cov_base_node* cov = static_cast*>(branch[1]); + const Type& v0 = vov->v0(); + const Type& v1 = vov->v1(); + const Type& v2 = cov->v (); + const Type c = cov->c (); + const details::operator_type o0 = vov->operation(); + const details::operator_type o1 = operation; + const details::operator_type o2 = cov->operation(); + + details::free_node(*(expr_gen.node_allocator_),branch[0]); + details::free_node(*(expr_gen.node_allocator_),branch[1]); + + expression_node_ptr result = error_node(); + + if (expr_gen.parser_->settings_.strength_reduction_enabled()) + { + // (v0 / v1) * (c / v2) --> (vocovov) (v0 * c) / (v1 * v2) + if ((details::e_div == o0) && (details::e_mul == o1) && (details::e_div == o2)) + { + const bool synthesis_result = + synthesize_sf4ext_expression:: + template compile(expr_gen, "(t*t)/(t*t)", v0, c, v1, v2, result); + + exprtk_debug(("(v0 / v1) * (c / v2) --> (vocovov) (v0 * c) / (v1 * v2)\n")); + + return (synthesis_result) ? result : error_node(); + } + // (v0 / v1) / (c / v2) --> (vovovoc) (v0 * v2) / (v1 * c) + if ((details::e_div == o0) && (details::e_div == o1) && (details::e_div == o2)) + { + const bool synthesis_result = + synthesize_sf4ext_expression:: + template compile(expr_gen, "(t*t)/(t*t)", v0, v2, v1, c, result); + + exprtk_debug(("(v0 / v1) / (c / v2) --> (vovovoc) (v0 * v2) / (v1 * c)\n")); + + return (synthesis_result) ? result : error_node(); + } + } + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1, o2), v0, v1, c, v2, result); + + if (synthesis_result) + return result; + + binary_functor_t f0 = reinterpret_cast(0); + binary_functor_t f1 = reinterpret_cast(0); + binary_functor_t f2 = reinterpret_cast(0); + + if (!expr_gen.valid_operator(o0,f0)) + return error_node(); + else if (!expr_gen.valid_operator(o1,f1)) + return error_node(); + else if (!expr_gen.valid_operator(o2,f2)) + return error_node(); + else + return node_type::allocate(*(expr_gen.node_allocator_), v0, v1, c, v2, f0, f1, f2); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1, + const details::operator_type o2) + { + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "(t" << expr_gen.to_str(o2) + << "t)"; + } + }; + + struct synthesize_vocovov_expression0 + { + typedef typename vocovov_t::type0 node_type; + typedef typename vocovov_t::sf4_type sf4_type; + typedef typename node_type::T0 T0; + typedef typename node_type::T1 T1; + typedef typename node_type::T2 T2; + typedef typename node_type::T3 T3; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // (v0 o0 c) o1 (v1 o2 v2) + const details::voc_base_node* voc = static_cast*>(branch[0]); + const details::vov_base_node* vov = static_cast*>(branch[1]); + const Type c = voc->c (); + const Type& v0 = voc->v (); + const Type& v1 = vov->v0(); + const Type& v2 = vov->v1(); + const details::operator_type o0 = voc->operation(); + const details::operator_type o1 = operation; + const details::operator_type o2 = vov->operation(); + + details::free_node(*(expr_gen.node_allocator_),branch[0]); + details::free_node(*(expr_gen.node_allocator_),branch[1]); + + expression_node_ptr result = error_node(); + + if (expr_gen.parser_->settings_.strength_reduction_enabled()) + { + // (v0 / c) * (v1 / v2) --> (vovocov) (v0 * v1) / (c * v2) + if ((details::e_div == o0) && (details::e_mul == o1) && (details::e_div == o2)) + { + const bool synthesis_result = + synthesize_sf4ext_expression:: + template compile(expr_gen, "(t*t)/(t*t)", v0, v1, c, v2, result); + + exprtk_debug(("(v0 / c) * (v1 / v2) --> (vovocov) (v0 * v1) / (c * v2)\n")); + + return (synthesis_result) ? result : error_node(); + } + // (v0 / c) / (v1 / v2) --> (vovocov) (v0 * v2) / (c * v1) + if ((details::e_div == o0) && (details::e_div == o1) && (details::e_div == o2)) + { + const bool synthesis_result = + synthesize_sf4ext_expression:: + template compile(expr_gen, "(t*t)/(t*t)", v0, v2, c, v1, result); + + exprtk_debug(("(v0 / c) / (v1 / v2) --> (vovocov) (v0 * v2) / (c * v1)\n")); + + return (synthesis_result) ? result : error_node(); + } + } + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1, o2), v0, c, v1, v2, result); + + if (synthesis_result) + return result; + + binary_functor_t f0 = reinterpret_cast(0); + binary_functor_t f1 = reinterpret_cast(0); + binary_functor_t f2 = reinterpret_cast(0); + + if (!expr_gen.valid_operator(o0,f0)) + return error_node(); + else if (!expr_gen.valid_operator(o1,f1)) + return error_node(); + else if (!expr_gen.valid_operator(o2,f2)) + return error_node(); + else + return node_type::allocate(*(expr_gen.node_allocator_), v0, c, v1, v2, f0, f1, f2); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1, + const details::operator_type o2) + { + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "(t" << expr_gen.to_str(o2) + << "t)"; + } + }; + + struct synthesize_covovov_expression0 + { + typedef typename covovov_t::type0 node_type; + typedef typename covovov_t::sf4_type sf4_type; + typedef typename node_type::T0 T0; + typedef typename node_type::T1 T1; + typedef typename node_type::T2 T2; + typedef typename node_type::T3 T3; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // (c o0 v0) o1 (v1 o2 v2) + const details::cov_base_node* cov = static_cast*>(branch[0]); + const details::vov_base_node* vov = static_cast*>(branch[1]); + const Type c = cov->c (); + const Type& v0 = cov->v (); + const Type& v1 = vov->v0(); + const Type& v2 = vov->v1(); + const details::operator_type o0 = cov->operation(); + const details::operator_type o1 = operation; + const details::operator_type o2 = vov->operation(); + + details::free_node(*(expr_gen.node_allocator_),branch[0]); + details::free_node(*(expr_gen.node_allocator_),branch[1]); + + expression_node_ptr result = error_node(); + + if (expr_gen.parser_->settings_.strength_reduction_enabled()) + { + // (c / v0) * (v1 / v2) --> (covovov) (c * v1) / (v0 * v2) + if ((details::e_div == o0) && (details::e_mul == o1) && (details::e_div == o2)) + { + const bool synthesis_result = + synthesize_sf4ext_expression:: + template compile(expr_gen, "(t*t)/(t*t)", c, v1, v0, v2, result); + + exprtk_debug(("(c / v0) * (v1 / v2) --> (covovov) (c * v1) / (v0 * v2)\n")); + + return (synthesis_result) ? result : error_node(); + } + // (c / v0) / (v1 / v2) --> (covovov) (c * v2) / (v0 * v1) + if ((details::e_div == o0) && (details::e_div == o1) && (details::e_div == o2)) + { + const bool synthesis_result = + synthesize_sf4ext_expression:: + template compile(expr_gen, "(t*t)/(t*t)", c, v2, v0, v1, result); + + exprtk_debug(("(c / v0) / (v1 / v2) --> (covovov) (c * v2) / (v0 * v1)\n")); + + return (synthesis_result) ? result : error_node(); + } + } + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1, o2), c, v0, v1, v2, result); + + if (synthesis_result) + return result; + + binary_functor_t f0 = reinterpret_cast(0); + binary_functor_t f1 = reinterpret_cast(0); + binary_functor_t f2 = reinterpret_cast(0); + + if (!expr_gen.valid_operator(o0,f0)) + return error_node(); + else if (!expr_gen.valid_operator(o1,f1)) + return error_node(); + else if (!expr_gen.valid_operator(o2,f2)) + return error_node(); + else + return node_type::allocate(*(expr_gen.node_allocator_), c, v0, v1, v2, f0, f1, f2); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1, + const details::operator_type o2) + { + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "(t" << expr_gen.to_str(o2) + << "t)"; + } + }; + + struct synthesize_covocov_expression0 + { + typedef typename covocov_t::type0 node_type; + typedef typename covocov_t::sf4_type sf4_type; + typedef typename node_type::T0 T0; + typedef typename node_type::T1 T1; + typedef typename node_type::T2 T2; + typedef typename node_type::T3 T3; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // (c0 o0 v0) o1 (c1 o2 v1) + const details::cov_base_node* cov0 = static_cast*>(branch[0]); + const details::cov_base_node* cov1 = static_cast*>(branch[1]); + const Type c0 = cov0->c(); + const Type& v0 = cov0->v(); + const Type c1 = cov1->c(); + const Type& v1 = cov1->v(); + const details::operator_type o0 = cov0->operation(); + const details::operator_type o1 = operation; + const details::operator_type o2 = cov1->operation(); + + details::free_node(*(expr_gen.node_allocator_),branch[0]); + details::free_node(*(expr_gen.node_allocator_),branch[1]); + + expression_node_ptr result = error_node(); + + if (expr_gen.parser_->settings_.strength_reduction_enabled()) + { + // (c0 + v0) + (c1 + v1) --> (covov) (c0 + c1) + v0 + v1 + if ((details::e_add == o0) && (details::e_add == o1) && (details::e_add == o2)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "(t+t)+t", (c0 + c1), v0, v1, result); + + exprtk_debug(("(c0 + v0) + (c1 + v1) --> (covov) (c0 + c1) + v0 + v1\n")); + + return (synthesis_result) ? result : error_node(); + } + // (c0 + v0) - (c1 + v1) --> (covov) (c0 - c1) + v0 - v1 + else if ((details::e_add == o0) && (details::e_sub == o1) && (details::e_add == o2)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "(t+t)-t", (c0 - c1), v0, v1, result); + + exprtk_debug(("(c0 + v0) - (c1 + v1) --> (covov) (c0 - c1) + v0 - v1\n")); + + return (synthesis_result) ? result : error_node(); + } + // (c0 - v0) - (c1 - v1) --> (covov) (c0 - c1) - v0 + v1 + else if ((details::e_sub == o0) && (details::e_sub == o1) && (details::e_sub == o2)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "(t-t)+t", (c0 - c1), v0, v1, result); + + exprtk_debug(("(c0 - v0) - (c1 - v1) --> (covov) (c0 - c1) - v0 + v1\n")); + + return (synthesis_result) ? result : error_node(); + } + // (c0 * v0) * (c1 * v1) --> (covov) (c0 * c1) * v0 * v1 + else if ((details::e_mul == o0) && (details::e_mul == o1) && (details::e_mul == o2)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "(t*t)*t", (c0 * c1), v0, v1, result); + + exprtk_debug(("(c0 * v0) * (c1 * v1) --> (covov) (c0 * c1) * v0 * v1\n")); + + return (synthesis_result) ? result : error_node(); + } + // (c0 * v0) / (c1 * v1) --> (covov) (c0 / c1) * (v0 / v1) + else if ((details::e_mul == o0) && (details::e_div == o1) && (details::e_mul == o2)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "(t*t)/t", (c0 / c1), v0, v1, result); + + exprtk_debug(("(c0 * v0) / (c1 * v1) --> (covov) (c0 / c1) * (v0 / v1)\n")); + + return (synthesis_result) ? result : error_node(); + } + // (c0 / v0) * (c1 / v1) --> (covov) (c0 * c1) / (v0 * v1) + else if ((details::e_div == o0) && (details::e_mul == o1) && (details::e_div == o2)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "t/(t*t)", (c0 * c1), v0, v1, result); + + exprtk_debug(("(c0 / v0) * (c1 / v1) --> (covov) (c0 * c1) / (v0 * v1)\n")); + + return (synthesis_result) ? result : error_node(); + } + // (c0 / v0) / (c1 / v1) --> (covov) ((c0 / c1) * v1) / v0 + else if ((details::e_div == o0) && (details::e_div == o1) && (details::e_div == o2)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "(t*t)/t", (c0 / c1), v1, v0, result); + + exprtk_debug(("(c0 / v0) / (c1 / v1) --> (covov) ((c0 / c1) * v1) / v0\n")); + + return (synthesis_result) ? result : error_node(); + } + // (c0 * v0) / (c1 / v1) --> (covov) (c0 / c1) * (v0 * v1) + else if ((details::e_mul == o0) && (details::e_div == o1) && (details::e_div == o2)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "t*(t*t)", (c0 / c1), v0, v1, result); + + exprtk_debug(("(c0 * v0) / (c1 / v1) --> (covov) (c0 / c1) * (v0 * v1)\n")); + + return (synthesis_result) ? result : error_node(); + } + // (c0 / v0) / (c1 * v1) --> (covov) (c0 / c1) / (v0 * v1) + else if ((details::e_div == o0) && (details::e_div == o1) && (details::e_mul == o2)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "t/(t*t)", (c0 / c1), v0, v1, result); + + exprtk_debug(("(c0 / v0) / (c1 * v1) --> (covov) (c0 / c1) / (v0 * v1)\n")); + + return (synthesis_result) ? result : error_node(); + } + // (c * v0) +/- (c * v1) --> (covov) c * (v0 +/- v1) + else if ( + (std::equal_to()(c0,c1)) && + (details::e_mul == o0) && + (details::e_mul == o2) && + ( + (details::e_add == o1) || + (details::e_sub == o1) + ) + ) + { + std::string specfunc; + + switch (o1) + { + case details::e_add : specfunc = "t*(t+t)"; break; + case details::e_sub : specfunc = "t*(t-t)"; break; + default : return error_node(); + } + + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, specfunc, c0, v0, v1, result); + + exprtk_debug(("(c * v0) +/- (c * v1) --> (covov) c * (v0 +/- v1)\n")); + + return (synthesis_result) ? result : error_node(); + } + } + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1, o2), c0, v0, c1, v1, result); + + if (synthesis_result) + return result; + + binary_functor_t f0 = reinterpret_cast(0); + binary_functor_t f1 = reinterpret_cast(0); + binary_functor_t f2 = reinterpret_cast(0); + + if (!expr_gen.valid_operator(o0,f0)) + return error_node(); + else if (!expr_gen.valid_operator(o1,f1)) + return error_node(); + else if (!expr_gen.valid_operator(o2,f2)) + return error_node(); + else + return node_type::allocate(*(expr_gen.node_allocator_), c0, v0, c1, v1, f0, f1, f2); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1, + const details::operator_type o2) + { + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "(t" << expr_gen.to_str(o2) + << "t)"; + } + }; + + struct synthesize_vocovoc_expression0 + { + typedef typename vocovoc_t::type0 node_type; + typedef typename vocovoc_t::sf4_type sf4_type; + typedef typename node_type::T0 T0; + typedef typename node_type::T1 T1; + typedef typename node_type::T2 T2; + typedef typename node_type::T3 T3; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // (v0 o0 c0) o1 (v1 o2 c1) + const details::voc_base_node* voc0 = static_cast*>(branch[0]); + const details::voc_base_node* voc1 = static_cast*>(branch[1]); + const Type c0 = voc0->c(); + const Type& v0 = voc0->v(); + const Type c1 = voc1->c(); + const Type& v1 = voc1->v(); + const details::operator_type o0 = voc0->operation(); + const details::operator_type o1 = operation; + const details::operator_type o2 = voc1->operation(); + + details::free_node(*(expr_gen.node_allocator_),branch[0]); + details::free_node(*(expr_gen.node_allocator_),branch[1]); + + expression_node_ptr result = error_node(); + + if (expr_gen.parser_->settings_.strength_reduction_enabled()) + { + // (v0 + c0) + (v1 + c1) --> (covov) (c0 + c1) + v0 + v1 + if ((details::e_add == o0) && (details::e_add == o1) && (details::e_add == o2)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "(t+t)+t", (c0 + c1), v0, v1, result); + + exprtk_debug(("(v0 + c0) + (v1 + c1) --> (covov) (c0 + c1) + v0 + v1\n")); + + return (synthesis_result) ? result : error_node(); + } + // (v0 + c0) - (v1 + c1) --> (covov) (c0 - c1) + v0 - v1 + else if ((details::e_add == o0) && (details::e_sub == o1) && (details::e_add == o2)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "(t+t)-t", (c0 - c1), v0, v1, result); + + exprtk_debug(("(v0 + c0) - (v1 + c1) --> (covov) (c0 - c1) + v0 - v1\n")); + + return (synthesis_result) ? result : error_node(); + } + // (v0 - c0) - (v1 - c1) --> (covov) (c1 - c0) + v0 - v1 + else if ((details::e_sub == o0) && (details::e_sub == o1) && (details::e_sub == o2)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "(t+t)-t", (c1 - c0), v0, v1, result); + + exprtk_debug(("(v0 - c0) - (v1 - c1) --> (covov) (c1 - c0) + v0 - v1\n")); + + return (synthesis_result) ? result : error_node(); + } + // (v0 * c0) * (v1 * c1) --> (covov) (c0 * c1) * v0 * v1 + else if ((details::e_mul == o0) && (details::e_mul == o1) && (details::e_mul == o2)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "(t*t)*t", (c0 * c1), v0, v1, result); + + exprtk_debug(("(v0 * c0) * (v1 * c1) --> (covov) (c0 * c1) * v0 * v1\n")); + + return (synthesis_result) ? result : error_node(); + } + // (v0 * c0) / (v1 * c1) --> (covov) (c0 / c1) * (v0 / v1) + else if ((details::e_mul == o0) && (details::e_div == o1) && (details::e_mul == o2)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "(t*t)/t", (c0 / c1), v0, v1, result); + + exprtk_debug(("(v0 * c0) / (v1 * c1) --> (covov) (c0 / c1) * (v0 / v1)\n")); + + return (synthesis_result) ? result : error_node(); + } + // (v0 / c0) * (v1 / c1) --> (covov) (1 / (c0 * c1)) * v0 * v1 + else if ((details::e_div == o0) && (details::e_mul == o1) && (details::e_div == o2)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "(t*t)*t", Type(1) / (c0 * c1), v0, v1, result); + + exprtk_debug(("(v0 / c0) * (v1 / c1) --> (covov) (1 / (c0 * c1)) * v0 * v1\n")); + + return (synthesis_result) ? result : error_node(); + } + // (v0 / c0) / (v1 / c1) --> (covov) ((c1 / c0) * v0) / v1 + else if ((details::e_div == o0) && (details::e_div == o1) && (details::e_div == o2)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "(t*t)/t", (c1 / c0), v0, v1, result); + + exprtk_debug(("(v0 / c0) / (v1 / c1) --> (covov) ((c1 / c0) * v0) / v1\n")); + + return (synthesis_result) ? result : error_node(); + } + // (v0 * c0) / (v1 / c1) --> (covov) (c0 * c1) * (v0 / v1) + else if ((details::e_mul == o0) && (details::e_div == o1) && (details::e_div == o2)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "t*(t/t)", (c0 * c1), v0, v1, result); + + exprtk_debug(("(v0 * c0) / (v1 / c1) --> (covov) (c0 * c1) * (v0 / v1)\n")); + + return (synthesis_result) ? result : error_node(); + } + // (v0 / c0) / (v1 * c1) --> (covov) (1 / (c0 * c1)) * v0 / v1 + else if ((details::e_div == o0) && (details::e_div == o1) && (details::e_mul == o2)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "t*(t/t)", Type(1) / (c0 * c1), v0, v1, result); + + exprtk_debug(("(v0 / c0) / (v1 * c1) --> (covov) (1 / (c0 * c1)) * v0 / v1\n")); + + return (synthesis_result) ? result : error_node(); + } + // (v0 / c0) * (v1 + c1) --> (vocovoc) (v0 * (1 / c0)) * (v1 + c1) + else if ((details::e_div == o0) && (details::e_mul == o1) && (details::e_add == o2)) + { + const bool synthesis_result = + synthesize_sf4ext_expression:: + template compile(expr_gen, "(t*t)*(t+t)", v0, T(1) / c0, v1, c1, result); + + exprtk_debug(("(v0 / c0) * (v1 + c1) --> (vocovoc) (v0 * (1 / c0)) * (v1 + c1)\n")); + + return (synthesis_result) ? result : error_node(); + } + // (v0 / c0) * (v1 - c1) --> (vocovoc) (v0 * (1 / c0)) * (v1 - c1) + else if ((details::e_div == o0) && (details::e_mul == o1) && (details::e_sub == o2)) + { + const bool synthesis_result = + synthesize_sf4ext_expression:: + template compile(expr_gen, "(t*t)*(t-t)", v0, T(1) / c0, v1, c1, result); + + exprtk_debug(("(v0 / c0) * (v1 - c1) --> (vocovoc) (v0 * (1 / c0)) * (v1 - c1)\n")); + + return (synthesis_result) ? result : error_node(); + } + // (v0 * c) +/- (v1 * c) --> (covov) c * (v0 +/- v1) + else if ( + (std::equal_to()(c0,c1)) && + (details::e_mul == o0) && + (details::e_mul == o2) && + ( + (details::e_add == o1) || + (details::e_sub == o1) + ) + ) + { + std::string specfunc; + + switch (o1) + { + case details::e_add : specfunc = "t*(t+t)"; break; + case details::e_sub : specfunc = "t*(t-t)"; break; + default : return error_node(); + } + + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, specfunc, c0, v0, v1, result); + + exprtk_debug(("(v0 * c) +/- (v1 * c) --> (covov) c * (v0 +/- v1)\n")); + + return (synthesis_result) ? result : error_node(); + } + // (v0 / c) +/- (v1 / c) --> (vovoc) (v0 +/- v1) / c + else if ( + (std::equal_to()(c0,c1)) && + (details::e_div == o0) && + (details::e_div == o2) && + ( + (details::e_add == o1) || + (details::e_sub == o1) + ) + ) + { + std::string specfunc; + + switch (o1) + { + case details::e_add : specfunc = "(t+t)/t"; break; + case details::e_sub : specfunc = "(t-t)/t"; break; + default : return error_node(); + } + + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, specfunc, v0, v1, c0, result); + + exprtk_debug(("(v0 / c) +/- (v1 / c) --> (vovoc) (v0 +/- v1) / c\n")); + + return (synthesis_result) ? result : error_node(); + } + } + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1, o2), v0, c0, v1, c1, result); + + if (synthesis_result) + return result; + + binary_functor_t f0 = reinterpret_cast(0); + binary_functor_t f1 = reinterpret_cast(0); + binary_functor_t f2 = reinterpret_cast(0); + + if (!expr_gen.valid_operator(o0,f0)) + return error_node(); + else if (!expr_gen.valid_operator(o1,f1)) + return error_node(); + else if (!expr_gen.valid_operator(o2,f2)) + return error_node(); + else + return node_type::allocate(*(expr_gen.node_allocator_), v0, c0, v1, c1, f0, f1, f2); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1, + const details::operator_type o2) + { + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "(t" << expr_gen.to_str(o2) + << "t)"; + } + }; + + struct synthesize_covovoc_expression0 + { + typedef typename covovoc_t::type0 node_type; + typedef typename covovoc_t::sf4_type sf4_type; + typedef typename node_type::T0 T0; + typedef typename node_type::T1 T1; + typedef typename node_type::T2 T2; + typedef typename node_type::T3 T3; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // (c0 o0 v0) o1 (v1 o2 c1) + const details::cov_base_node* cov = static_cast*>(branch[0]); + const details::voc_base_node* voc = static_cast*>(branch[1]); + const Type c0 = cov->c(); + const Type& v0 = cov->v(); + const Type c1 = voc->c(); + const Type& v1 = voc->v(); + const details::operator_type o0 = cov->operation(); + const details::operator_type o1 = operation; + const details::operator_type o2 = voc->operation(); + + details::free_node(*(expr_gen.node_allocator_),branch[0]); + details::free_node(*(expr_gen.node_allocator_),branch[1]); + + expression_node_ptr result = error_node(); + + if (expr_gen.parser_->settings_.strength_reduction_enabled()) + { + // (c0 + v0) + (v1 + c1) --> (covov) (c0 + c1) + v0 + v1 + if ((details::e_add == o0) && (details::e_add == o1) && (details::e_add == o2)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "(t+t)+t", (c0 + c1), v0, v1, result); + + exprtk_debug(("(c0 + v0) + (v1 + c1) --> (covov) (c0 + c1) + v0 + v1\n")); + + return (synthesis_result) ? result : error_node(); + } + // (c0 + v0) - (v1 + c1) --> (covov) (c0 - c1) + v0 - v1 + else if ((details::e_add == o0) && (details::e_sub == o1) && (details::e_add == o2)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "(t+t)-t", (c0 - c1), v0, v1, result); + + exprtk_debug(("(c0 + v0) - (v1 + c1) --> (covov) (c0 - c1) + v0 - v1\n")); + + return (synthesis_result) ? result : error_node(); + } + // (c0 - v0) - (v1 - c1) --> (covov) (c0 + c1) - v0 - v1 + else if ((details::e_sub == o0) && (details::e_sub == o1) && (details::e_sub == o2)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "t-(t+t)", (c0 + c1), v0, v1, result); + + exprtk_debug(("(c0 - v0) - (v1 - c1) --> (covov) (c0 + c1) - v0 - v1\n")); + + return (synthesis_result) ? result : error_node(); + } + // (c0 * v0) * (v1 * c1) --> (covov) (c0 * c1) * v0 * v1 + else if ((details::e_mul == o0) && (details::e_mul == o1) && (details::e_mul == o2)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "(t*t)*t", (c0 * c1), v0, v1, result); + + exprtk_debug(("(c0 * v0) * (v1 * c1) --> (covov) (c0 * c1) * v0 * v1\n")); + + return (synthesis_result) ? result : error_node(); + } + // (c0 * v0) / (v1 * c1) --> (covov) (c0 / c1) * (v0 / v1) + else if ((details::e_mul == o0) && (details::e_div == o1) && (details::e_mul == o2)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "(t*t)/t", (c0 / c1), v0, v1, result); + + exprtk_debug(("(c0 * v0) / (v1 * c1) --> (covov) (c0 / c1) * (v0 / v1)\n")); + + return (synthesis_result) ? result : error_node(); + } + // (c0 / v0) * (v1 / c1) --> (covov) (c0 / c1) * (v1 / v0) + else if ((details::e_div == o0) && (details::e_mul == o1) && (details::e_div == o2)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "t*(t/t)", (c0 / c1), v1, v0, result); + + exprtk_debug(("(c0 / v0) * (v1 / c1) --> (covov) (c0 / c1) * (v1 / v0)\n")); + + return (synthesis_result) ? result : error_node(); + } + // (c0 / v0) / (v1 / c1) --> (covov) (c0 * c1) / (v0 * v1) + else if ((details::e_div == o0) && (details::e_div == o1) && (details::e_div == o2)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "t/(t*t)", (c0 * c1), v0, v1, result); + + exprtk_debug(("(c0 / v0) / (v1 / c1) --> (covov) (c0 * c1) / (v0 * v1)\n")); + + return (synthesis_result) ? result : error_node(); + } + // (c0 * v0) / (v1 / c1) --> (covov) (c0 * c1) * (v0 / v1) + else if ((details::e_mul == o0) && (details::e_div == o1) && (details::e_div == o2)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "(t*t)/t", (c0 * c1), v0, v1, result); + + exprtk_debug(("(c0 * v0) / (v1 / c1) --> (covov) (c0 * c1) * (v0 / v1)\n")); + + return (synthesis_result) ? result : error_node(); + } + // (c0 / v0) / (v1 * c1) --> (covov) (c0 / c1) / (v0 * v1) + else if ((details::e_div == o0) && (details::e_div == o1) && (details::e_mul == o2)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "t/(t*t)", (c0 / c1), v0, v1, result); + + exprtk_debug(("(c0 / v0) / (v1 * c1) --> (covov) (c0 / c1) / (v0 * v1)\n")); + + return (synthesis_result) ? result : error_node(); + } + // (c * v0) +/- (v1 * c) --> (covov) c * (v0 +/- v1) + else if ( + (std::equal_to()(c0,c1)) && + (details::e_mul == o0) && + (details::e_mul == o2) && + ( + (details::e_add == o1) || + (details::e_sub == o1) + ) + ) + { + std::string specfunc; + + switch (o1) + { + case details::e_add : specfunc = "t*(t+t)"; break; + case details::e_sub : specfunc = "t*(t-t)"; break; + default : return error_node(); + } + + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, specfunc, c0, v0, v1, result); + + exprtk_debug(("(c * v0) +/- (v1 * c) --> (covov) c * (v0 +/- v1)\n")); + + return (synthesis_result) ? result : error_node(); + } + } + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1, o2), c0, v0, v1, c1, result); + + if (synthesis_result) + return result; + + binary_functor_t f0 = reinterpret_cast(0); + binary_functor_t f1 = reinterpret_cast(0); + binary_functor_t f2 = reinterpret_cast(0); + + if (!expr_gen.valid_operator(o0,f0)) + return error_node(); + else if (!expr_gen.valid_operator(o1,f1)) + return error_node(); + else if (!expr_gen.valid_operator(o2,f2)) + return error_node(); + else + return node_type::allocate(*(expr_gen.node_allocator_), c0, v0, v1, c1, f0, f1, f2); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1, + const details::operator_type o2) + { + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "(t" << expr_gen.to_str(o2) + << "t)"; + } + }; + + struct synthesize_vococov_expression0 + { + typedef typename vococov_t::type0 node_type; + typedef typename vococov_t::sf4_type sf4_type; + typedef typename node_type::T0 T0; + typedef typename node_type::T1 T1; + typedef typename node_type::T2 T2; + typedef typename node_type::T3 T3; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // (v0 o0 c0) o1 (c1 o2 v1) + const details::voc_base_node* voc = static_cast*>(branch[0]); + const details::cov_base_node* cov = static_cast*>(branch[1]); + const Type c0 = voc->c(); + const Type& v0 = voc->v(); + const Type c1 = cov->c(); + const Type& v1 = cov->v(); + const details::operator_type o0 = voc->operation(); + const details::operator_type o1 = operation; + const details::operator_type o2 = cov->operation(); + + details::free_node(*(expr_gen.node_allocator_),branch[0]); + details::free_node(*(expr_gen.node_allocator_),branch[1]); + + expression_node_ptr result = error_node(); + + if (expr_gen.parser_->settings_.strength_reduction_enabled()) + { + // (v0 + c0) + (c1 + v1) --> (covov) (c0 + c1) + v0 + v1 + if ((details::e_add == o0) && (details::e_add == o1) && (details::e_add == o2)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "(t+t)+t", (c0 + c1), v0, v1, result); + + exprtk_debug(("(v0 + c0) + (c1 + v1) --> (covov) (c0 + c1) + v0 + v1\n")); + + return (synthesis_result) ? result : error_node(); + } + // (v0 + c0) - (c1 + v1) --> (covov) (c0 - c1) + v0 - v1 + else if ((details::e_add == o0) && (details::e_sub == o1) && (details::e_add == o2)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "(t+t)-t", (c0 - c1), v0, v1, result); + + exprtk_debug(("(v0 + c0) - (c1 + v1) --> (covov) (c0 - c1) + v0 - v1\n")); + + return (synthesis_result) ? result : error_node(); + } + // (v0 - c0) - (c1 - v1) --> (vovoc) v0 + v1 - (c1 + c0) + else if ((details::e_sub == o0) && (details::e_sub == o1) && (details::e_sub == o2)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "(t+t)-t", v0, v1, (c1 + c0), result); + + exprtk_debug(("(v0 - c0) - (c1 - v1) --> (vovoc) v0 + v1 - (c1 + c0)\n")); + + return (synthesis_result) ? result : error_node(); + } + // (v0 * c0) * (c1 * v1) --> (covov) (c0 * c1) * v0 * v1 + else if ((details::e_mul == o0) && (details::e_mul == o1) && (details::e_mul == o2)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "(t*t)*t", (c0 * c1), v0, v1, result); + + exprtk_debug(("(v0 * c0) * (c1 * v1) --> (covov) (c0 * c1) * v0 * v1\n")); + + return (synthesis_result) ? result : error_node(); + } + // (v0 * c0) / (c1 * v1) --> (covov) (c0 / c1) * (v0 * v1) + else if ((details::e_mul == o0) && (details::e_div == o1) && (details::e_mul == o2)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "(t*t)/t", (c0 / c1), v0, v1, result); + + exprtk_debug(("(v0 * c0) / (c1 * v1) --> (covov) (c0 / c1) * (v0 * v1)\n")); + + return (synthesis_result) ? result : error_node(); + } + // (v0 / c0) * (c1 / v1) --> (covov) (c1 / c0) * (v0 / v1) + else if ((details::e_div == o0) && (details::e_mul == o1) && (details::e_div == o2)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "(t*t)/t", (c1 / c0), v0, v1, result); + + exprtk_debug(("(v0 / c0) * (c1 / v1) --> (covov) (c1 / c0) * (v0 / v1)\n")); + + return (synthesis_result) ? result : error_node(); + } + // (v0 * c0) / (c1 / v1) --> (covov) (c0 / c1) * (v0 * v1) + else if ((details::e_mul == o0) && (details::e_div == o1) && (details::e_div == o2)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "(t*t)*t", (c0 / c1), v0, v1, result); + + exprtk_debug(("(v0 * c0) / (c1 / v1) --> (covov) (c0 / c1) * (v0 * v1)\n")); + + return (synthesis_result) ? result : error_node(); + } + // (v0 / c0) / (c1 * v1) --> (covov) (1 / (c0 * c1)) * (v0 / v1) + else if ((details::e_div == o0) && (details::e_div == o1) && (details::e_mul == o2)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "(t*t)/t", Type(1) / (c0 * c1), v0, v1, result); + + exprtk_debug(("(v0 / c0) / (c1 * v1) --> (covov) (1 / (c0 * c1)) * (v0 / v1)\n")); + + return (synthesis_result) ? result : error_node(); + } + // (v0 / c0) / (c1 / v1) --> (vovoc) (v0 * v1) * (1 / (c0 * c1)) + else if ((details::e_div == o0) && (details::e_div == o1) && (details::e_div == o2)) + { + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, "(t*t)*t", v0, v1, Type(1) / (c0 * c1), result); + + exprtk_debug(("(v0 / c0) / (c1 / v1) --> (vovoc) (v0 * v1) * (1 / (c0 * c1))\n")); + + return (synthesis_result) ? result : error_node(); + } + // (v0 * c) +/- (c * v1) --> (covov) c * (v0 +/- v1) + else if ( + (std::equal_to()(c0,c1)) && + (details::e_mul == o0) && + (details::e_mul == o2) && + ( + (details::e_add == o1) || (details::e_sub == o1) + ) + ) + { + std::string specfunc; + + switch (o1) + { + case details::e_add : specfunc = "t*(t+t)"; break; + case details::e_sub : specfunc = "t*(t-t)"; break; + default : return error_node(); + } + + const bool synthesis_result = + synthesize_sf3ext_expression:: + template compile(expr_gen, specfunc, c0, v0, v1, result); + + exprtk_debug(("(v0 * c) +/- (c * v1) --> (covov) c * (v0 +/- v1)\n")); + + return (synthesis_result) ? result : error_node(); + } + } + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1, o2), v0, c0, c1, v1, result); + + if (synthesis_result) + return result; + + binary_functor_t f0 = reinterpret_cast(0); + binary_functor_t f1 = reinterpret_cast(0); + binary_functor_t f2 = reinterpret_cast(0); + + if (!expr_gen.valid_operator(o0,f0)) + return error_node(); + else if (!expr_gen.valid_operator(o1,f1)) + return error_node(); + else if (!expr_gen.valid_operator(o2,f2)) + return error_node(); + else + return node_type::allocate(*(expr_gen.node_allocator_), v0, c0, c1, v1, f0, f1, f2); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1, + const details::operator_type o2) + { + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "(t" << expr_gen.to_str(o2) + << "t)"; + } + }; + + struct synthesize_vovovov_expression1 + { + typedef typename vovovov_t::type1 node_type; + typedef typename vovovov_t::sf4_type sf4_type; + typedef typename node_type::T0 T0; + typedef typename node_type::T1 T1; + typedef typename node_type::T2 T2; + typedef typename node_type::T3 T3; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // v0 o0 (v1 o1 (v2 o2 v3)) + typedef typename synthesize_vovov_expression1::node_type lcl_vovov_t; + + const lcl_vovov_t* vovov = static_cast(branch[1]); + const Type& v0 = static_cast*>(branch[0])->ref(); + const Type& v1 = vovov->t0(); + const Type& v2 = vovov->t1(); + const Type& v3 = vovov->t2(); + const details::operator_type o0 = operation; + const details::operator_type o1 = expr_gen.get_operator(vovov->f0()); + const details::operator_type o2 = expr_gen.get_operator(vovov->f1()); + + binary_functor_t f0 = reinterpret_cast(0); + binary_functor_t f1 = vovov->f0(); + binary_functor_t f2 = vovov->f1(); + + details::free_node(*(expr_gen.node_allocator_),branch[1]); + + expression_node_ptr result = error_node(); + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile + (expr_gen,id(expr_gen, o0, o1, o2), v0, v1, v2, v3, result); + + if (synthesis_result) + return result; + else if (!expr_gen.valid_operator(o0,f0)) + return error_node(); + + exprtk_debug(("v0 o0 (v1 o1 (v2 o2 v3))\n")); + + return node_type::allocate(*(expr_gen.node_allocator_), v0, v1, v2, v3, f0, f1, f2); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1, + const details::operator_type o2) + { + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "(t" << expr_gen.to_str(o2) + << "t))"; + } + }; + + struct synthesize_vovovoc_expression1 + { + typedef typename vovovoc_t::type1 node_type; + typedef typename vovovoc_t::sf4_type sf4_type; + typedef typename node_type::T0 T0; + typedef typename node_type::T1 T1; + typedef typename node_type::T2 T2; + typedef typename node_type::T3 T3; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // v0 o0 (v1 o1 (v2 o2 c)) + typedef typename synthesize_vovoc_expression1::node_type lcl_vovoc_t; + + const lcl_vovoc_t* vovoc = static_cast(branch[1]); + const Type& v0 = static_cast*>(branch[0])->ref(); + const Type& v1 = vovoc->t0(); + const Type& v2 = vovoc->t1(); + const Type c = vovoc->t2(); + const details::operator_type o0 = operation; + const details::operator_type o1 = expr_gen.get_operator(vovoc->f0()); + const details::operator_type o2 = expr_gen.get_operator(vovoc->f1()); + + binary_functor_t f0 = reinterpret_cast(0); + binary_functor_t f1 = vovoc->f0(); + binary_functor_t f2 = vovoc->f1(); + + details::free_node(*(expr_gen.node_allocator_),branch[1]); + + expression_node_ptr result = error_node(); + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1, o2), v0, v1, v2, c, result); + + if (synthesis_result) + return result; + else if (!expr_gen.valid_operator(o0,f0)) + return error_node(); + + exprtk_debug(("v0 o0 (v1 o1 (v2 o2 c))\n")); + + return node_type::allocate(*(expr_gen.node_allocator_), v0, v1, v2, c, f0, f1, f2); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1, + const details::operator_type o2) + { + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "(t" << expr_gen.to_str(o2) + << "t))"; + } + }; + + struct synthesize_vovocov_expression1 + { + typedef typename vovocov_t::type1 node_type; + typedef typename vovocov_t::sf4_type sf4_type; + typedef typename node_type::T0 T0; + typedef typename node_type::T1 T1; + typedef typename node_type::T2 T2; + typedef typename node_type::T3 T3; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // v0 o0 (v1 o1 (c o2 v2)) + typedef typename synthesize_vocov_expression1::node_type lcl_vocov_t; + + const lcl_vocov_t* vocov = static_cast(branch[1]); + const Type& v0 = static_cast*>(branch[0])->ref(); + const Type& v1 = vocov->t0(); + const Type c = vocov->t1(); + const Type& v2 = vocov->t2(); + const details::operator_type o0 = operation; + const details::operator_type o1 = expr_gen.get_operator(vocov->f0()); + const details::operator_type o2 = expr_gen.get_operator(vocov->f1()); + + binary_functor_t f0 = reinterpret_cast(0); + binary_functor_t f1 = vocov->f0(); + binary_functor_t f2 = vocov->f1(); + + details::free_node(*(expr_gen.node_allocator_),branch[1]); + + expression_node_ptr result = error_node(); + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1, o2), v0, v1, c, v2, result); + + if (synthesis_result) + return result; + if (!expr_gen.valid_operator(o0,f0)) + return error_node(); + + exprtk_debug(("v0 o0 (v1 o1 (c o2 v2))\n")); + + return node_type::allocate(*(expr_gen.node_allocator_), v0, v1, c, v2, f0, f1, f2); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1, + const details::operator_type o2) + { + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "(t" << expr_gen.to_str(o2) + << "t))"; + } + }; + + struct synthesize_vocovov_expression1 + { + typedef typename vocovov_t::type1 node_type; + typedef typename vocovov_t::sf4_type sf4_type; + typedef typename node_type::T0 T0; + typedef typename node_type::T1 T1; + typedef typename node_type::T2 T2; + typedef typename node_type::T3 T3; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // v0 o0 (c o1 (v1 o2 v2)) + typedef typename synthesize_covov_expression1::node_type lcl_covov_t; + + const lcl_covov_t* covov = static_cast(branch[1]); + const Type& v0 = static_cast*>(branch[0])->ref(); + const Type c = covov->t0(); + const Type& v1 = covov->t1(); + const Type& v2 = covov->t2(); + const details::operator_type o0 = operation; + const details::operator_type o1 = expr_gen.get_operator(covov->f0()); + const details::operator_type o2 = expr_gen.get_operator(covov->f1()); + + binary_functor_t f0 = reinterpret_cast(0); + binary_functor_t f1 = covov->f0(); + binary_functor_t f2 = covov->f1(); + + details::free_node(*(expr_gen.node_allocator_),branch[1]); + + expression_node_ptr result = error_node(); + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1, o2), v0, c, v1, v2, result); + + if (synthesis_result) + return result; + else if (!expr_gen.valid_operator(o0,f0)) + return error_node(); + + exprtk_debug(("v0 o0 (c o1 (v1 o2 v2))\n")); + + return node_type::allocate(*(expr_gen.node_allocator_), v0, c, v1, v2, f0, f1, f2); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1, + const details::operator_type o2) + { + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "(t" << expr_gen.to_str(o2) + << "t))"; + } + }; + + struct synthesize_covovov_expression1 + { + typedef typename covovov_t::type1 node_type; + typedef typename covovov_t::sf4_type sf4_type; + typedef typename node_type::T0 T0; + typedef typename node_type::T1 T1; + typedef typename node_type::T2 T2; + typedef typename node_type::T3 T3; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // c o0 (v0 o1 (v1 o2 v2)) + typedef typename synthesize_vovov_expression1::node_type lcl_vovov_t; + + const lcl_vovov_t* vovov = static_cast(branch[1]); + const Type c = static_cast*>(branch[0])->value(); + const Type& v0 = vovov->t0(); + const Type& v1 = vovov->t1(); + const Type& v2 = vovov->t2(); + const details::operator_type o0 = operation; + const details::operator_type o1 = expr_gen.get_operator(vovov->f0()); + const details::operator_type o2 = expr_gen.get_operator(vovov->f1()); + + binary_functor_t f0 = reinterpret_cast(0); + binary_functor_t f1 = vovov->f0(); + binary_functor_t f2 = vovov->f1(); + + details::free_node(*(expr_gen.node_allocator_),branch[0]); + details::free_node(*(expr_gen.node_allocator_),branch[1]); + + expression_node_ptr result = error_node(); + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1, o2), c, v0, v1, v2, result); + + if (synthesis_result) + return result; + if (!expr_gen.valid_operator(o0,f0)) + return error_node(); + + exprtk_debug(("c o0 (v0 o1 (v1 o2 v2))\n")); + + return node_type::allocate(*(expr_gen.node_allocator_), c, v0, v1, v2, f0, f1, f2); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1, + const details::operator_type o2) + { + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "(t" << expr_gen.to_str(o2) + << "t))"; + } + }; + + struct synthesize_covocov_expression1 + { + typedef typename covocov_t::type1 node_type; + typedef typename covocov_t::sf4_type sf4_type; + typedef typename node_type::T0 T0; + typedef typename node_type::T1 T1; + typedef typename node_type::T2 T2; + typedef typename node_type::T3 T3; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // c0 o0 (v0 o1 (c1 o2 v1)) + typedef typename synthesize_vocov_expression1::node_type lcl_vocov_t; + + const lcl_vocov_t* vocov = static_cast(branch[1]); + const Type c0 = static_cast*>(branch[0])->value(); + const Type& v0 = vocov->t0(); + const Type c1 = vocov->t1(); + const Type& v1 = vocov->t2(); + const details::operator_type o0 = operation; + const details::operator_type o1 = expr_gen.get_operator(vocov->f0()); + const details::operator_type o2 = expr_gen.get_operator(vocov->f1()); + + binary_functor_t f0 = reinterpret_cast(0); + binary_functor_t f1 = vocov->f0(); + binary_functor_t f2 = vocov->f1(); + + details::free_node(*(expr_gen.node_allocator_),branch[0]); + details::free_node(*(expr_gen.node_allocator_),branch[1]); + + expression_node_ptr result = error_node(); + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1, o2), c0, v0, c1, v1, result); + + if (synthesis_result) + return result; + else if (!expr_gen.valid_operator(o0,f0)) + return error_node(); + + exprtk_debug(("c0 o0 (v0 o1 (c1 o2 v1))\n")); + + return node_type::allocate(*(expr_gen.node_allocator_), c0, v0, c1, v1, f0, f1, f2); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1, + const details::operator_type o2) + { + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "(t" << expr_gen.to_str(o2) + << "t))"; + } + }; + + struct synthesize_vocovoc_expression1 + { + typedef typename vocovoc_t::type1 node_type; + typedef typename vocovoc_t::sf4_type sf4_type; + typedef typename node_type::T0 T0; + typedef typename node_type::T1 T1; + typedef typename node_type::T2 T2; + typedef typename node_type::T3 T3; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // v0 o0 (c0 o1 (v1 o2 c2)) + typedef typename synthesize_covoc_expression1::node_type lcl_covoc_t; + + const lcl_covoc_t* covoc = static_cast(branch[1]); + const Type& v0 = static_cast*>(branch[0])->ref(); + const Type c0 = covoc->t0(); + const Type& v1 = covoc->t1(); + const Type c1 = covoc->t2(); + const details::operator_type o0 = operation; + const details::operator_type o1 = expr_gen.get_operator(covoc->f0()); + const details::operator_type o2 = expr_gen.get_operator(covoc->f1()); + + binary_functor_t f0 = reinterpret_cast(0); + binary_functor_t f1 = covoc->f0(); + binary_functor_t f2 = covoc->f1(); + + details::free_node(*(expr_gen.node_allocator_),branch[1]); + + expression_node_ptr result = error_node(); + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1, o2), v0, c0, v1, c1, result); + + if (synthesis_result) + return result; + else if (!expr_gen.valid_operator(o0,f0)) + return error_node(); + + exprtk_debug(("v0 o0 (c0 o1 (v1 o2 c2))\n")); + + return node_type::allocate(*(expr_gen.node_allocator_), v0, c0, v1, c1, f0, f1, f2); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1, + const details::operator_type o2) + { + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "(t" << expr_gen.to_str(o2) + << "t))"; + } + }; + + struct synthesize_covovoc_expression1 + { + typedef typename covovoc_t::type1 node_type; + typedef typename covovoc_t::sf4_type sf4_type; + typedef typename node_type::T0 T0; + typedef typename node_type::T1 T1; + typedef typename node_type::T2 T2; + typedef typename node_type::T3 T3; + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // c0 o0 (v0 o1 (v1 o2 c1)) + typedef typename synthesize_vovoc_expression1::node_type lcl_vovoc_t; + + const lcl_vovoc_t* vovoc = static_cast(branch[1]); + const Type c0 = static_cast*>(branch[0])->value(); + const Type& v0 = vovoc->t0(); + const Type& v1 = vovoc->t1(); + const Type c1 = vovoc->t2(); + const details::operator_type o0 = operation; + const details::operator_type o1 = expr_gen.get_operator(vovoc->f0()); + const details::operator_type o2 = expr_gen.get_operator(vovoc->f1()); + + binary_functor_t f0 = reinterpret_cast(0); + binary_functor_t f1 = vovoc->f0(); + binary_functor_t f2 = vovoc->f1(); + + details::free_node(*(expr_gen.node_allocator_),branch[0]); + details::free_node(*(expr_gen.node_allocator_),branch[1]); + + expression_node_ptr result = error_node(); + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1, o2), c0, v0, v1, c1, result); + + if (synthesis_result) + return result; + else if (!expr_gen.valid_operator(o0,f0)) + return error_node(); + + exprtk_debug(("c0 o0 (v0 o1 (v1 o2 c1))\n")); + + return node_type::allocate(*(expr_gen.node_allocator_), c0, v0, v1, c1, f0, f1, f2); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1, + const details::operator_type o2) + { + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "(t" << expr_gen.to_str(o2) + << "t))"; + } + }; + + struct synthesize_vococov_expression1 + { + typedef typename vococov_t::type1 node_type; + typedef typename vococov_t::sf4_type sf4_type; + typedef typename node_type::T0 T0; + typedef typename node_type::T1 T1; + typedef typename node_type::T2 T2; + typedef typename node_type::T3 T3; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // v0 o0 (c0 o1 (c1 o2 v1)) + typedef typename synthesize_cocov_expression1::node_type lcl_cocov_t; + + const lcl_cocov_t* cocov = static_cast(branch[1]); + const Type& v0 = static_cast*>(branch[0])->ref(); + const Type c0 = cocov->t0(); + const Type c1 = cocov->t1(); + const Type& v1 = cocov->t2(); + const details::operator_type o0 = operation; + const details::operator_type o1 = expr_gen.get_operator(cocov->f0()); + const details::operator_type o2 = expr_gen.get_operator(cocov->f1()); + + binary_functor_t f0 = reinterpret_cast(0); + binary_functor_t f1 = cocov->f0(); + binary_functor_t f2 = cocov->f1(); + + details::free_node(*(expr_gen.node_allocator_),branch[1]); + + expression_node_ptr result = error_node(); + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1, o2), v0, c0, c1, v1, result); + + if (synthesis_result) + return result; + else if (!expr_gen.valid_operator(o0,f0)) + return error_node(); + + exprtk_debug(("v0 o0 (c0 o1 (c1 o2 v1))\n")); + + return node_type::allocate(*(expr_gen.node_allocator_), v0, c0, c1, v1, f0, f1, f2); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1, + const details::operator_type o2) + { + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "(t" << expr_gen.to_str(o2) + << "t))"; + } + }; + + struct synthesize_vovovov_expression2 + { + typedef typename vovovov_t::type2 node_type; + typedef typename vovovov_t::sf4_type sf4_type; + typedef typename node_type::T0 T0; + typedef typename node_type::T1 T1; + typedef typename node_type::T2 T2; + typedef typename node_type::T3 T3; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // v0 o0 ((v1 o1 v2) o2 v3) + typedef typename synthesize_vovov_expression0::node_type lcl_vovov_t; + + const lcl_vovov_t* vovov = static_cast(branch[1]); + const Type& v0 = static_cast*>(branch[0])->ref(); + const Type& v1 = vovov->t0(); + const Type& v2 = vovov->t1(); + const Type& v3 = vovov->t2(); + const details::operator_type o0 = operation; + const details::operator_type o1 = expr_gen.get_operator(vovov->f0()); + const details::operator_type o2 = expr_gen.get_operator(vovov->f1()); + + binary_functor_t f0 = reinterpret_cast(0); + binary_functor_t f1 = vovov->f0(); + binary_functor_t f2 = vovov->f1(); + + details::free_node(*(expr_gen.node_allocator_),branch[1]); + + expression_node_ptr result = error_node(); + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1, o2), v0, v1, v2, v3, result); + + if (synthesis_result) + return result; + else if (!expr_gen.valid_operator(o0,f0)) + return error_node(); + + exprtk_debug(("v0 o0 ((v1 o1 v2) o2 v3)\n")); + + return node_type::allocate(*(expr_gen.node_allocator_), v0, v1, v2, v3, f0, f1, f2); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1, + const details::operator_type o2) + { + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "((t" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t)"; + } + }; + + struct synthesize_vovovoc_expression2 + { + typedef typename vovovoc_t::type2 node_type; + typedef typename vovovoc_t::sf4_type sf4_type; + typedef typename node_type::T0 T0; + typedef typename node_type::T1 T1; + typedef typename node_type::T2 T2; + typedef typename node_type::T3 T3; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // v0 o0 ((v1 o1 v2) o2 c) + typedef typename synthesize_vovoc_expression0::node_type lcl_vovoc_t; + + const lcl_vovoc_t* vovoc = static_cast(branch[1]); + const Type& v0 = static_cast*>(branch[0])->ref(); + const Type& v1 = vovoc->t0(); + const Type& v2 = vovoc->t1(); + const Type c = vovoc->t2(); + const details::operator_type o0 = operation; + const details::operator_type o1 = expr_gen.get_operator(vovoc->f0()); + const details::operator_type o2 = expr_gen.get_operator(vovoc->f1()); + + binary_functor_t f0 = reinterpret_cast(0); + binary_functor_t f1 = vovoc->f0(); + binary_functor_t f2 = vovoc->f1(); + + details::free_node(*(expr_gen.node_allocator_),branch[1]); + + expression_node_ptr result = error_node(); + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1, o2), v0, v1, v2, c, result); + + if (synthesis_result) + return result; + else if (!expr_gen.valid_operator(o0,f0)) + return error_node(); + + exprtk_debug(("v0 o0 ((v1 o1 v2) o2 c)\n")); + + return node_type::allocate(*(expr_gen.node_allocator_), v0, v1, v2, c, f0, f1, f2); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1, + const details::operator_type o2) + { + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "((t" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t)"; + } + }; + + struct synthesize_vovocov_expression2 + { + typedef typename vovocov_t::type2 node_type; + typedef typename vovocov_t::sf4_type sf4_type; + typedef typename node_type::T0 T0; + typedef typename node_type::T1 T1; + typedef typename node_type::T2 T2; + typedef typename node_type::T3 T3; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // v0 o0 ((v1 o1 c) o2 v2) + typedef typename synthesize_vocov_expression0::node_type lcl_vocov_t; + + const lcl_vocov_t* vocov = static_cast(branch[1]); + const Type& v0 = static_cast*>(branch[0])->ref(); + const Type& v1 = vocov->t0(); + const Type c = vocov->t1(); + const Type& v2 = vocov->t2(); + const details::operator_type o0 = operation; + const details::operator_type o1 = expr_gen.get_operator(vocov->f0()); + const details::operator_type o2 = expr_gen.get_operator(vocov->f1()); + + binary_functor_t f0 = reinterpret_cast(0); + binary_functor_t f1 = vocov->f0(); + binary_functor_t f2 = vocov->f1(); + + details::free_node(*(expr_gen.node_allocator_),branch[1]); + + expression_node_ptr result = error_node(); + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1, o2), v0, v1, c, v2, result); + + if (synthesis_result) + return result; + else if (!expr_gen.valid_operator(o0,f0)) + return error_node(); + + exprtk_debug(("v0 o0 ((v1 o1 c) o2 v2)\n")); + + return node_type::allocate(*(expr_gen.node_allocator_), v0, v1, c, v2, f0, f1, f2); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1, + const details::operator_type o2) + { + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "((t" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t)"; + } + }; + + struct synthesize_vocovov_expression2 + { + typedef typename vocovov_t::type2 node_type; + typedef typename vocovov_t::sf4_type sf4_type; + typedef typename node_type::T0 T0; + typedef typename node_type::T1 T1; + typedef typename node_type::T2 T2; + typedef typename node_type::T3 T3; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // v0 o0 ((c o1 v1) o2 v2) + typedef typename synthesize_covov_expression0::node_type lcl_covov_t; + + const lcl_covov_t* covov = static_cast(branch[1]); + const Type& v0 = static_cast*>(branch[0])->ref(); + const Type c = covov->t0(); + const Type& v1 = covov->t1(); + const Type& v2 = covov->t2(); + const details::operator_type o0 = operation; + const details::operator_type o1 = expr_gen.get_operator(covov->f0()); + const details::operator_type o2 = expr_gen.get_operator(covov->f1()); + + binary_functor_t f0 = reinterpret_cast(0); + binary_functor_t f1 = covov->f0(); + binary_functor_t f2 = covov->f1(); + + details::free_node(*(expr_gen.node_allocator_),branch[1]); + + expression_node_ptr result = error_node(); + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1, o2), v0, c, v1, v2, result); + + if (synthesis_result) + return result; + else if (!expr_gen.valid_operator(o0,f0)) + return error_node(); + + exprtk_debug(("v0 o0 ((c o1 v1) o2 v2)\n")); + + return node_type::allocate(*(expr_gen.node_allocator_), v0, c, v1, v2, f0, f1, f2); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1, + const details::operator_type o2) + { + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "((t" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t)"; + } + }; + + struct synthesize_covovov_expression2 + { + typedef typename covovov_t::type2 node_type; + typedef typename covovov_t::sf4_type sf4_type; + typedef typename node_type::T0 T0; + typedef typename node_type::T1 T1; + typedef typename node_type::T2 T2; + typedef typename node_type::T3 T3; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // c o0 ((v1 o1 v2) o2 v3) + typedef typename synthesize_vovov_expression0::node_type lcl_vovov_t; + + const lcl_vovov_t* vovov = static_cast(branch[1]); + const Type c = static_cast*>(branch[0])->value(); + const Type& v0 = vovov->t0(); + const Type& v1 = vovov->t1(); + const Type& v2 = vovov->t2(); + const details::operator_type o0 = operation; + const details::operator_type o1 = expr_gen.get_operator(vovov->f0()); + const details::operator_type o2 = expr_gen.get_operator(vovov->f1()); + + binary_functor_t f0 = reinterpret_cast(0); + binary_functor_t f1 = vovov->f0(); + binary_functor_t f2 = vovov->f1(); + + details::free_node(*(expr_gen.node_allocator_),branch[0]); + details::free_node(*(expr_gen.node_allocator_),branch[1]); + + expression_node_ptr result = error_node(); + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1, o2), c, v0, v1, v2, result); + + if (synthesis_result) + return result; + else if (!expr_gen.valid_operator(o0,f0)) + return error_node(); + + exprtk_debug(("c o0 ((v1 o1 v2) o2 v3)\n")); + + return node_type::allocate(*(expr_gen.node_allocator_), c, v0, v1, v2, f0, f1, f2); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1, + const details::operator_type o2) + { + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "((t" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t)"; + } + }; + + struct synthesize_covocov_expression2 + { + typedef typename covocov_t::type2 node_type; + typedef typename covocov_t::sf4_type sf4_type; + typedef typename node_type::T0 T0; + typedef typename node_type::T1 T1; + typedef typename node_type::T2 T2; + typedef typename node_type::T3 T3; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // c0 o0 ((v0 o1 c1) o2 v1) + typedef typename synthesize_vocov_expression0::node_type lcl_vocov_t; + + const lcl_vocov_t* vocov = static_cast(branch[1]); + const Type c0 = static_cast*>(branch[0])->value(); + const Type& v0 = vocov->t0(); + const Type c1 = vocov->t1(); + const Type& v1 = vocov->t2(); + const details::operator_type o0 = operation; + const details::operator_type o1 = expr_gen.get_operator(vocov->f0()); + const details::operator_type o2 = expr_gen.get_operator(vocov->f1()); + + binary_functor_t f0 = reinterpret_cast(0); + binary_functor_t f1 = vocov->f0(); + binary_functor_t f2 = vocov->f1(); + + details::free_node(*(expr_gen.node_allocator_),branch[0]); + details::free_node(*(expr_gen.node_allocator_),branch[1]); + + expression_node_ptr result = error_node(); + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1, o2), c0, v0, c1, v1, result); + + if (synthesis_result) + return result; + else if (!expr_gen.valid_operator(o0,f0)) + return error_node(); + + exprtk_debug(("c0 o0 ((v0 o1 c1) o2 v1)\n")); + + return node_type::allocate(*(expr_gen.node_allocator_), c0, v0, c1, v1, f0, f1, f2); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1, + const details::operator_type o2) + { + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "((t" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t)"; + } + }; + + struct synthesize_vocovoc_expression2 + { + typedef typename vocovoc_t::type2 node_type; + typedef typename vocovoc_t::sf4_type sf4_type; + typedef typename node_type::T0 T0; + typedef typename node_type::T1 T1; + typedef typename node_type::T2 T2; + typedef typename node_type::T3 T3; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // v0 o0 ((c0 o1 v1) o2 c1) + typedef typename synthesize_covoc_expression0::node_type lcl_covoc_t; + + const lcl_covoc_t* covoc = static_cast(branch[1]); + const Type& v0 = static_cast*>(branch[0])->ref(); + const Type c0 = covoc->t0(); + const Type& v1 = covoc->t1(); + const Type c1 = covoc->t2(); + const details::operator_type o0 = operation; + const details::operator_type o1 = expr_gen.get_operator(covoc->f0()); + const details::operator_type o2 = expr_gen.get_operator(covoc->f1()); + + binary_functor_t f0 = reinterpret_cast(0); + binary_functor_t f1 = covoc->f0(); + binary_functor_t f2 = covoc->f1(); + + details::free_node(*(expr_gen.node_allocator_),branch[1]); + + expression_node_ptr result = error_node(); + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1, o2), v0, c0, v1, c1, result); + + if (synthesis_result) + return result; + else if (!expr_gen.valid_operator(o0,f0)) + return error_node(); + + exprtk_debug(("v0 o0 ((c0 o1 v1) o2 c1)\n")); + + return node_type::allocate(*(expr_gen.node_allocator_), v0, c0, v1, c1, f0, f1, f2); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1, + const details::operator_type o2) + { + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "((t" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t)"; + } + }; + + struct synthesize_covovoc_expression2 + { + typedef typename covovoc_t::type2 node_type; + typedef typename covovoc_t::sf4_type sf4_type; + typedef typename node_type::T0 T0; + typedef typename node_type::T1 T1; + typedef typename node_type::T2 T2; + typedef typename node_type::T3 T3; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // c0 o0 ((v0 o1 v1) o2 c1) + typedef typename synthesize_vovoc_expression0::node_type lcl_vovoc_t; + + const lcl_vovoc_t* vovoc = static_cast(branch[1]); + const Type c0 = static_cast*>(branch[0])->value(); + const Type& v0 = vovoc->t0(); + const Type& v1 = vovoc->t1(); + const Type c1 = vovoc->t2(); + const details::operator_type o0 = operation; + const details::operator_type o1 = expr_gen.get_operator(vovoc->f0()); + const details::operator_type o2 = expr_gen.get_operator(vovoc->f1()); + + binary_functor_t f0 = reinterpret_cast(0); + binary_functor_t f1 = vovoc->f0(); + binary_functor_t f2 = vovoc->f1(); + + details::free_node(*(expr_gen.node_allocator_),branch[0]); + details::free_node(*(expr_gen.node_allocator_),branch[1]); + + expression_node_ptr result = error_node(); + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1, o2), c0, v0, v1, c1, result); + + if (synthesis_result) + return result; + else if (!expr_gen.valid_operator(o0,f0)) + return error_node(); + + exprtk_debug(("c0 o0 ((v0 o1 v1) o2 c1)\n")); + + return node_type::allocate(*(expr_gen.node_allocator_), c0, v0, v1, c1, f0, f1, f2); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1, + const details::operator_type o2) + { + return details::build_string() + << "t" << expr_gen.to_str(o0) + << "((t" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t)"; + } + }; + + struct synthesize_vococov_expression2 + { + typedef typename vococov_t::type2 node_type; + static inline expression_node_ptr process(expression_generator&, + const details::operator_type&, + expression_node_ptr (&)[2]) + { + // v0 o0 ((c0 o1 c1) o2 v1) - Not possible + exprtk_debug(("v0 o0 ((c0 o1 c1) o2 v1) - Not possible\n")); + return error_node(); + } + + static inline std::string id(expression_generator&, + const details::operator_type, + const details::operator_type, + const details::operator_type) + { + return "INVALID"; + } + }; + + struct synthesize_vovovov_expression3 + { + typedef typename vovovov_t::type3 node_type; + typedef typename vovovov_t::sf4_type sf4_type; + typedef typename node_type::T0 T0; + typedef typename node_type::T1 T1; + typedef typename node_type::T2 T2; + typedef typename node_type::T3 T3; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // ((v0 o0 v1) o1 v2) o2 v3 + typedef typename synthesize_vovov_expression0::node_type lcl_vovov_t; + + const lcl_vovov_t* vovov = static_cast(branch[0]); + const Type& v0 = vovov->t0(); + const Type& v1 = vovov->t1(); + const Type& v2 = vovov->t2(); + const Type& v3 = static_cast*>(branch[1])->ref(); + const details::operator_type o0 = expr_gen.get_operator(vovov->f0()); + const details::operator_type o1 = expr_gen.get_operator(vovov->f1()); + const details::operator_type o2 = operation; + + binary_functor_t f0 = vovov->f0(); + binary_functor_t f1 = vovov->f1(); + binary_functor_t f2 = reinterpret_cast(0); + + details::free_node(*(expr_gen.node_allocator_),branch[0]); + + expression_node_ptr result = error_node(); + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1, o2), v0, v1, v2, v3, result); + + if (synthesis_result) + return result; + else if (!expr_gen.valid_operator(o2,f2)) + return error_node(); + + exprtk_debug(("((v0 o0 v1) o1 v2) o2 v3\n")); + + return node_type::allocate(*(expr_gen.node_allocator_), v0, v1, v2, v3, f0, f1, f2); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1, + const details::operator_type o2) + { + return details::build_string() + << "((t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t"; + } + }; + + struct synthesize_vovovoc_expression3 + { + typedef typename vovovoc_t::type3 node_type; + typedef typename vovovoc_t::sf4_type sf4_type; + typedef typename node_type::T0 T0; + typedef typename node_type::T1 T1; + typedef typename node_type::T2 T2; + typedef typename node_type::T3 T3; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // ((v0 o0 v1) o1 v2) o2 c + typedef typename synthesize_vovov_expression0::node_type lcl_vovov_t; + + const lcl_vovov_t* vovov = static_cast(branch[0]); + const Type& v0 = vovov->t0(); + const Type& v1 = vovov->t1(); + const Type& v2 = vovov->t2(); + const Type c = static_cast*>(branch[1])->value(); + const details::operator_type o0 = expr_gen.get_operator(vovov->f0()); + const details::operator_type o1 = expr_gen.get_operator(vovov->f1()); + const details::operator_type o2 = operation; + + binary_functor_t f0 = vovov->f0(); + binary_functor_t f1 = vovov->f1(); + binary_functor_t f2 = reinterpret_cast(0); + + details::free_node(*(expr_gen.node_allocator_),branch[0]); + details::free_node(*(expr_gen.node_allocator_),branch[1]); + + expression_node_ptr result = error_node(); + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1, o2), v0, v1, v2, c, result); + + if (synthesis_result) + return result; + else if (!expr_gen.valid_operator(o2,f2)) + return error_node(); + + exprtk_debug(("((v0 o0 v1) o1 v2) o2 c\n")); + + return node_type::allocate(*(expr_gen.node_allocator_), v0, v1, v2, c, f0, f1, f2); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1, + const details::operator_type o2) + { + return details::build_string() + << "((t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t"; + } + }; + + struct synthesize_vovocov_expression3 + { + typedef typename vovocov_t::type3 node_type; + typedef typename vovocov_t::sf4_type sf4_type; + typedef typename node_type::T0 T0; + typedef typename node_type::T1 T1; + typedef typename node_type::T2 T2; + typedef typename node_type::T3 T3; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // ((v0 o0 v1) o1 c) o2 v2 + typedef typename synthesize_vovoc_expression0::node_type lcl_vovoc_t; + + const lcl_vovoc_t* vovoc = static_cast(branch[0]); + const Type& v0 = vovoc->t0(); + const Type& v1 = vovoc->t1(); + const Type c = vovoc->t2(); + const Type& v2 = static_cast*>(branch[1])->ref(); + const details::operator_type o0 = expr_gen.get_operator(vovoc->f0()); + const details::operator_type o1 = expr_gen.get_operator(vovoc->f1()); + const details::operator_type o2 = operation; + + binary_functor_t f0 = vovoc->f0(); + binary_functor_t f1 = vovoc->f1(); + binary_functor_t f2 = reinterpret_cast(0); + + details::free_node(*(expr_gen.node_allocator_),branch[0]); + + expression_node_ptr result = error_node(); + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1, o2), v0, v1, c, v2, result); + + if (synthesis_result) + return result; + else if (!expr_gen.valid_operator(o2,f2)) + return error_node(); + + exprtk_debug(("((v0 o0 v1) o1 c) o2 v2\n")); + + return node_type::allocate(*(expr_gen.node_allocator_), v0, v1, c, v2, f0, f1, f2); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1, + const details::operator_type o2) + { + return details::build_string() + << "((t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t"; + } + }; + + struct synthesize_vocovov_expression3 + { + typedef typename vocovov_t::type3 node_type; + typedef typename vocovov_t::sf4_type sf4_type; + typedef typename node_type::T0 T0; + typedef typename node_type::T1 T1; + typedef typename node_type::T2 T2; + typedef typename node_type::T3 T3; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // ((v0 o0 c) o1 v1) o2 v2 + typedef typename synthesize_vocov_expression0::node_type lcl_vocov_t; + + const lcl_vocov_t* vocov = static_cast(branch[0]); + const Type& v0 = vocov->t0(); + const Type c = vocov->t1(); + const Type& v1 = vocov->t2(); + const Type& v2 = static_cast*>(branch[1])->ref(); + const details::operator_type o0 = expr_gen.get_operator(vocov->f0()); + const details::operator_type o1 = expr_gen.get_operator(vocov->f1()); + const details::operator_type o2 = operation; + + binary_functor_t f0 = vocov->f0(); + binary_functor_t f1 = vocov->f1(); + binary_functor_t f2 = reinterpret_cast(0); + + details::free_node(*(expr_gen.node_allocator_),branch[0]); + + expression_node_ptr result = error_node(); + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1, o2), v0, c, v1, v2, result); + + if (synthesis_result) + return result; + else if (!expr_gen.valid_operator(o2,f2)) + return error_node(); + + exprtk_debug(("((v0 o0 c) o1 v1) o2 v2\n")); + + return node_type::allocate(*(expr_gen.node_allocator_), v0, c, v1, v2, f0, f1, f2); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1, + const details::operator_type o2) + { + return details::build_string() + << "((t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t"; + } + }; + + struct synthesize_covovov_expression3 + { + typedef typename covovov_t::type3 node_type; + typedef typename covovov_t::sf4_type sf4_type; + typedef typename node_type::T0 T0; + typedef typename node_type::T1 T1; + typedef typename node_type::T2 T2; + typedef typename node_type::T3 T3; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // ((c o0 v0) o1 v1) o2 v2 + typedef typename synthesize_covov_expression0::node_type lcl_covov_t; + + const lcl_covov_t* covov = static_cast(branch[0]); + const Type c = covov->t0(); + const Type& v0 = covov->t1(); + const Type& v1 = covov->t2(); + const Type& v2 = static_cast*>(branch[1])->ref(); + const details::operator_type o0 = expr_gen.get_operator(covov->f0()); + const details::operator_type o1 = expr_gen.get_operator(covov->f1()); + const details::operator_type o2 = operation; + + binary_functor_t f0 = covov->f0(); + binary_functor_t f1 = covov->f1(); + binary_functor_t f2 = reinterpret_cast(0); + + details::free_node(*(expr_gen.node_allocator_),branch[0]); + + expression_node_ptr result = error_node(); + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1, o2), c, v0, v1, v2, result); + + if (synthesis_result) + return result; + else if (!expr_gen.valid_operator(o2,f2)) + return error_node(); + + exprtk_debug(("((c o0 v0) o1 v1) o2 v2\n")); + + return node_type::allocate(*(expr_gen.node_allocator_), c, v0, v1, v2, f0, f1, f2); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1, + const details::operator_type o2) + { + return details::build_string() + << "((t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t"; + } + }; + + struct synthesize_covocov_expression3 + { + typedef typename covocov_t::type3 node_type; + typedef typename covocov_t::sf4_type sf4_type; + typedef typename node_type::T0 T0; + typedef typename node_type::T1 T1; + typedef typename node_type::T2 T2; + typedef typename node_type::T3 T3; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // ((c0 o0 v0) o1 c1) o2 v1 + typedef typename synthesize_covoc_expression0::node_type lcl_covoc_t; + + const lcl_covoc_t* covoc = static_cast(branch[0]); + const Type c0 = covoc->t0(); + const Type& v0 = covoc->t1(); + const Type c1 = covoc->t2(); + const Type& v1 = static_cast*>(branch[1])->ref(); + const details::operator_type o0 = expr_gen.get_operator(covoc->f0()); + const details::operator_type o1 = expr_gen.get_operator(covoc->f1()); + const details::operator_type o2 = operation; + + binary_functor_t f0 = covoc->f0(); + binary_functor_t f1 = covoc->f1(); + binary_functor_t f2 = reinterpret_cast(0); + + details::free_node(*(expr_gen.node_allocator_),branch[0]); + + expression_node_ptr result = error_node(); + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1, o2), c0, v0, c1, v1, result); + + if (synthesis_result) + return result; + else if (!expr_gen.valid_operator(o2,f2)) + return error_node(); + + exprtk_debug(("((c0 o0 v0) o1 c1) o2 v1\n")); + + return node_type::allocate(*(expr_gen.node_allocator_), c0, v0, c1, v1, f0, f1, f2); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1, + const details::operator_type o2) + { + return details::build_string() + << "((t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t"; + } + }; + + struct synthesize_vocovoc_expression3 + { + typedef typename vocovoc_t::type3 node_type; + typedef typename vocovoc_t::sf4_type sf4_type; + typedef typename node_type::T0 T0; + typedef typename node_type::T1 T1; + typedef typename node_type::T2 T2; + typedef typename node_type::T3 T3; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // ((v0 o0 c0) o1 v1) o2 c1 + typedef typename synthesize_vocov_expression0::node_type lcl_vocov_t; + + const lcl_vocov_t* vocov = static_cast(branch[0]); + const Type& v0 = vocov->t0(); + const Type c0 = vocov->t1(); + const Type& v1 = vocov->t2(); + const Type c1 = static_cast*>(branch[1])->value(); + const details::operator_type o0 = expr_gen.get_operator(vocov->f0()); + const details::operator_type o1 = expr_gen.get_operator(vocov->f1()); + const details::operator_type o2 = operation; + + binary_functor_t f0 = vocov->f0(); + binary_functor_t f1 = vocov->f1(); + binary_functor_t f2 = reinterpret_cast(0); + + details::free_node(*(expr_gen.node_allocator_),branch[0]); + details::free_node(*(expr_gen.node_allocator_),branch[1]); + + expression_node_ptr result = error_node(); + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1, o2), v0, c0, v1, c1, result); + + if (synthesis_result) + return result; + else if (!expr_gen.valid_operator(o2,f2)) + return error_node(); + + exprtk_debug(("((v0 o0 c0) o1 v1) o2 c1\n")); + + return node_type::allocate(*(expr_gen.node_allocator_), v0, c0, v1, c1, f0, f1, f2); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1, + const details::operator_type o2) + { + return details::build_string() + << "((t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t"; + } + }; + + struct synthesize_covovoc_expression3 + { + typedef typename covovoc_t::type3 node_type; + typedef typename covovoc_t::sf4_type sf4_type; + typedef typename node_type::T0 T0; + typedef typename node_type::T1 T1; + typedef typename node_type::T2 T2; + typedef typename node_type::T3 T3; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // ((c0 o0 v0) o1 v1) o2 c1 + typedef typename synthesize_covov_expression0::node_type lcl_covov_t; + + const lcl_covov_t* covov = static_cast(branch[0]); + const Type c0 = covov->t0(); + const Type& v0 = covov->t1(); + const Type& v1 = covov->t2(); + const Type c1 = static_cast*>(branch[1])->value(); + const details::operator_type o0 = expr_gen.get_operator(covov->f0()); + const details::operator_type o1 = expr_gen.get_operator(covov->f1()); + const details::operator_type o2 = operation; + + binary_functor_t f0 = covov->f0(); + binary_functor_t f1 = covov->f1(); + binary_functor_t f2 = reinterpret_cast(0); + + details::free_node(*(expr_gen.node_allocator_),branch[0]); + details::free_node(*(expr_gen.node_allocator_),branch[1]); + + expression_node_ptr result = error_node(); + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1, o2), c0, v0, v1, c1, result); + + if (synthesis_result) + return result; + else if (!expr_gen.valid_operator(o2,f2)) + return error_node(); + + exprtk_debug(("((c0 o0 v0) o1 v1) o2 c1\n")); + + return node_type::allocate(*(expr_gen.node_allocator_), c0, v0, v1, c1, f0, f1, f2); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1, + const details::operator_type o2) + { + return details::build_string() + << "((t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t"; + } + }; + + struct synthesize_vococov_expression3 + { + typedef typename vococov_t::type3 node_type; + typedef typename vococov_t::sf4_type sf4_type; + typedef typename node_type::T0 T0; + typedef typename node_type::T1 T1; + typedef typename node_type::T2 T2; + typedef typename node_type::T3 T3; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // ((v0 o0 c0) o1 c1) o2 v1 + typedef typename synthesize_vococ_expression0::node_type lcl_vococ_t; + + const lcl_vococ_t* vococ = static_cast(branch[0]); + const Type& v0 = vococ->t0(); + const Type c0 = vococ->t1(); + const Type c1 = vococ->t2(); + const Type& v1 = static_cast*>(branch[1])->ref(); + const details::operator_type o0 = expr_gen.get_operator(vococ->f0()); + const details::operator_type o1 = expr_gen.get_operator(vococ->f1()); + const details::operator_type o2 = operation; + + binary_functor_t f0 = vococ->f0(); + binary_functor_t f1 = vococ->f1(); + binary_functor_t f2 = reinterpret_cast(0); + + details::free_node(*(expr_gen.node_allocator_),branch[0]); + + expression_node_ptr result = error_node(); + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1, o2), v0, c0, c1, v1, result); + + if (synthesis_result) + return result; + else if (!expr_gen.valid_operator(o2,f2)) + return error_node(); + + exprtk_debug(("((v0 o0 c0) o1 c1) o2 v1\n")); + + return node_type::allocate(*(expr_gen.node_allocator_), v0, c0, c1, v1, f0, f1, f2); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1, + const details::operator_type o2) + { + return details::build_string() + << "((t" << expr_gen.to_str(o0) + << "t)" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t"; + } + }; + + struct synthesize_vovovov_expression4 + { + typedef typename vovovov_t::type4 node_type; + typedef typename vovovov_t::sf4_type sf4_type; + typedef typename node_type::T0 T0; + typedef typename node_type::T1 T1; + typedef typename node_type::T2 T2; + typedef typename node_type::T3 T3; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // (v0 o0 (v1 o1 v2)) o2 v3 + typedef typename synthesize_vovov_expression1::node_type lcl_vovov_t; + + const lcl_vovov_t* vovov = static_cast(branch[0]); + const Type& v0 = vovov->t0(); + const Type& v1 = vovov->t1(); + const Type& v2 = vovov->t2(); + const Type& v3 = static_cast*>(branch[1])->ref(); + const details::operator_type o0 = expr_gen.get_operator(vovov->f0()); + const details::operator_type o1 = expr_gen.get_operator(vovov->f1()); + const details::operator_type o2 = operation; + + binary_functor_t f0 = vovov->f0(); + binary_functor_t f1 = vovov->f1(); + binary_functor_t f2 = reinterpret_cast(0); + + details::free_node(*(expr_gen.node_allocator_),branch[0]); + + expression_node_ptr result = error_node(); + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1, o2), v0, v1, v2, v3, result); + + if (synthesis_result) + return result; + else if (!expr_gen.valid_operator(o2,f2)) + return error_node(); + + exprtk_debug(("(v0 o0 (v1 o1 v2)) o2 v3\n")); + + return node_type::allocate(*(expr_gen.node_allocator_), v0, v1, v2, v3, f0, f1, f2); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1, + const details::operator_type o2) + { + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t"; + } + }; + + struct synthesize_vovovoc_expression4 + { + typedef typename vovovoc_t::type4 node_type; + typedef typename vovovoc_t::sf4_type sf4_type; + typedef typename node_type::T0 T0; + typedef typename node_type::T1 T1; + typedef typename node_type::T2 T2; + typedef typename node_type::T3 T3; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // ((v0 o0 (v1 o1 v2)) o2 c) + typedef typename synthesize_vovov_expression1::node_type lcl_vovov_t; + + const lcl_vovov_t* vovov = static_cast(branch[0]); + const Type& v0 = vovov->t0(); + const Type& v1 = vovov->t1(); + const Type& v2 = vovov->t2(); + const Type c = static_cast*>(branch[1])->value(); + const details::operator_type o0 = expr_gen.get_operator(vovov->f0()); + const details::operator_type o1 = expr_gen.get_operator(vovov->f1()); + const details::operator_type o2 = operation; + + binary_functor_t f0 = vovov->f0(); + binary_functor_t f1 = vovov->f1(); + binary_functor_t f2 = reinterpret_cast(0); + + details::free_node(*(expr_gen.node_allocator_),branch[0]); + details::free_node(*(expr_gen.node_allocator_),branch[1]); + + expression_node_ptr result = error_node(); + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1, o2), v0, v1, v2, c, result); + + if (synthesis_result) + return result; + else if (!expr_gen.valid_operator(o2,f2)) + return error_node(); + + exprtk_debug(("((v0 o0 (v1 o1 v2)) o2 c)\n")); + + return node_type::allocate(*(expr_gen.node_allocator_), v0, v1, v2, c, f0, f1, f2); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1, + const details::operator_type o2) + { + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t"; + } + }; + + struct synthesize_vovocov_expression4 + { + typedef typename vovocov_t::type4 node_type; + typedef typename vovocov_t::sf4_type sf4_type; + typedef typename node_type::T0 T0; + typedef typename node_type::T1 T1; + typedef typename node_type::T2 T2; + typedef typename node_type::T3 T3; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // ((v0 o0 (v1 o1 c)) o2 v1) + typedef typename synthesize_vovoc_expression1::node_type lcl_vovoc_t; + + const lcl_vovoc_t* vovoc = static_cast(branch[0]); + const Type& v0 = vovoc->t0(); + const Type& v1 = vovoc->t1(); + const Type c = vovoc->t2(); + const Type& v2 = static_cast*>(branch[1])->ref(); + const details::operator_type o0 = expr_gen.get_operator(vovoc->f0()); + const details::operator_type o1 = expr_gen.get_operator(vovoc->f1()); + const details::operator_type o2 = operation; + + binary_functor_t f0 = vovoc->f0(); + binary_functor_t f1 = vovoc->f1(); + binary_functor_t f2 = reinterpret_cast(0); + + details::free_node(*(expr_gen.node_allocator_),branch[0]); + + expression_node_ptr result = error_node(); + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1, o2), v0, v1, c, v2, result); + + if (synthesis_result) + return result; + else if (!expr_gen.valid_operator(o2,f2)) + return error_node(); + + exprtk_debug(("((v0 o0 (v1 o1 c)) o2 v1)\n")); + + return node_type::allocate(*(expr_gen.node_allocator_), v0, v1, c, v2, f0, f1, f2); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1, + const details::operator_type o2) + { + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t"; + } + }; + + struct synthesize_vocovov_expression4 + { + typedef typename vocovov_t::type4 node_type; + typedef typename vocovov_t::sf4_type sf4_type; + typedef typename node_type::T0 T0; + typedef typename node_type::T1 T1; + typedef typename node_type::T2 T2; + typedef typename node_type::T3 T3; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // ((v0 o0 (c o1 v1)) o2 v2) + typedef typename synthesize_vocov_expression1::node_type lcl_vocov_t; + + const lcl_vocov_t* vocov = static_cast(branch[0]); + const Type& v0 = vocov->t0(); + const Type c = vocov->t1(); + const Type& v1 = vocov->t2(); + const Type& v2 = static_cast*>(branch[1])->ref(); + const details::operator_type o0 = expr_gen.get_operator(vocov->f0()); + const details::operator_type o1 = expr_gen.get_operator(vocov->f1()); + const details::operator_type o2 = operation; + + binary_functor_t f0 = vocov->f0(); + binary_functor_t f1 = vocov->f1(); + binary_functor_t f2 = reinterpret_cast(0); + + details::free_node(*(expr_gen.node_allocator_),branch[0]); + expression_node_ptr result = error_node(); + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1, o2), v0, c, v1, v2, result); + + if (synthesis_result) + return result; + else if (!expr_gen.valid_operator(o2,f2)) + return error_node(); + + exprtk_debug(("((v0 o0 (c o1 v1)) o2 v2)\n")); + + return node_type::allocate(*(expr_gen.node_allocator_), v0, c, v1, v2, f0, f1, f2); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1, + const details::operator_type o2) + { + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t"; + } + }; + + struct synthesize_covovov_expression4 + { + typedef typename covovov_t::type4 node_type; + typedef typename covovov_t::sf4_type sf4_type; + typedef typename node_type::T0 T0; + typedef typename node_type::T1 T1; + typedef typename node_type::T2 T2; + typedef typename node_type::T3 T3; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // ((c o0 (v0 o1 v1)) o2 v2) + typedef typename synthesize_covov_expression1::node_type lcl_covov_t; + + const lcl_covov_t* covov = static_cast(branch[0]); + const Type c = covov->t0(); + const Type& v0 = covov->t1(); + const Type& v1 = covov->t2(); + const Type& v2 = static_cast*>(branch[1])->ref(); + const details::operator_type o0 = expr_gen.get_operator(covov->f0()); + const details::operator_type o1 = expr_gen.get_operator(covov->f1()); + const details::operator_type o2 = operation; + + binary_functor_t f0 = covov->f0(); + binary_functor_t f1 = covov->f1(); + binary_functor_t f2 = reinterpret_cast(0); + + details::free_node(*(expr_gen.node_allocator_),branch[0]); + + expression_node_ptr result = error_node(); + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1, o2), c, v0, v1, v2, result); + + if (synthesis_result) + return result; + else if (!expr_gen.valid_operator(o2,f2)) + return error_node(); + + exprtk_debug(("((c o0 (v0 o1 v1)) o2 v2)\n")); + + return node_type::allocate(*(expr_gen.node_allocator_), c, v0, v1, v2, f0, f1, f2); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1, + const details::operator_type o2) + { + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t"; + } + }; + + struct synthesize_covocov_expression4 + { + typedef typename covocov_t::type4 node_type; + typedef typename covocov_t::sf4_type sf4_type; + typedef typename node_type::T0 T0; + typedef typename node_type::T1 T1; + typedef typename node_type::T2 T2; + typedef typename node_type::T3 T3; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // ((c0 o0 (v0 o1 c1)) o2 v1) + typedef typename synthesize_covoc_expression1::node_type lcl_covoc_t; + + const lcl_covoc_t* covoc = static_cast(branch[0]); + const Type c0 = covoc->t0(); + const Type& v0 = covoc->t1(); + const Type c1 = covoc->t2(); + const Type& v1 = static_cast*>(branch[1])->ref(); + const details::operator_type o0 = expr_gen.get_operator(covoc->f0()); + const details::operator_type o1 = expr_gen.get_operator(covoc->f1()); + const details::operator_type o2 = operation; + + binary_functor_t f0 = covoc->f0(); + binary_functor_t f1 = covoc->f1(); + binary_functor_t f2 = reinterpret_cast(0); + + details::free_node(*(expr_gen.node_allocator_),branch[0]); + + expression_node_ptr result = error_node(); + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1, o2), c0, v0, c1, v1, result); + + if (synthesis_result) + return result; + else if (!expr_gen.valid_operator(o2,f2)) + return error_node(); + + exprtk_debug(("((c0 o0 (v0 o1 c1)) o2 v1)\n")); + + return node_type::allocate(*(expr_gen.node_allocator_), c0, v0, c1, v1, f0, f1, f2); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1, + const details::operator_type o2) + { + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t"; + } + }; + + struct synthesize_vocovoc_expression4 + { + typedef typename vocovoc_t::type4 node_type; + typedef typename vocovoc_t::sf4_type sf4_type; + typedef typename node_type::T0 T0; + typedef typename node_type::T1 T1; + typedef typename node_type::T2 T2; + typedef typename node_type::T3 T3; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // ((v0 o0 (c0 o1 v1)) o2 c1) + typedef typename synthesize_vocov_expression1::node_type lcl_vocov_t; + + const lcl_vocov_t* vocov = static_cast(branch[0]); + const Type& v0 = vocov->t0(); + const Type c0 = vocov->t1(); + const Type& v1 = vocov->t2(); + const Type c1 = static_cast*>(branch[1])->value(); + const details::operator_type o0 = expr_gen.get_operator(vocov->f0()); + const details::operator_type o1 = expr_gen.get_operator(vocov->f1()); + const details::operator_type o2 = operation; + + binary_functor_t f0 = vocov->f0(); + binary_functor_t f1 = vocov->f1(); + binary_functor_t f2 = reinterpret_cast(0); + + details::free_node(*(expr_gen.node_allocator_),branch[0]); + details::free_node(*(expr_gen.node_allocator_),branch[1]); + + expression_node_ptr result = error_node(); + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1, o2), v0, c0, v1, c1, result); + + if (synthesis_result) + return result; + else if (!expr_gen.valid_operator(o2,f2)) + return error_node(); + + exprtk_debug(("((v0 o0 (c0 o1 v1)) o2 c1)\n")); + + return node_type::allocate(*(expr_gen.node_allocator_), v0, c0, v1, c1, f0, f1, f2); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1, + const details::operator_type o2) + { + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t"; + } + }; + + struct synthesize_covovoc_expression4 + { + typedef typename covovoc_t::type4 node_type; + typedef typename covovoc_t::sf4_type sf4_type; + typedef typename node_type::T0 T0; + typedef typename node_type::T1 T1; + typedef typename node_type::T2 T2; + typedef typename node_type::T3 T3; + + static inline expression_node_ptr process(expression_generator& expr_gen, + const details::operator_type& operation, + expression_node_ptr (&branch)[2]) + { + // ((c0 o0 (v0 o1 v1)) o2 c1) + typedef typename synthesize_covov_expression1::node_type lcl_covov_t; + + const lcl_covov_t* covov = static_cast(branch[0]); + const Type c0 = covov->t0(); + const Type& v0 = covov->t1(); + const Type& v1 = covov->t2(); + const Type c1 = static_cast*>(branch[1])->value(); + const details::operator_type o0 = expr_gen.get_operator(covov->f0()); + const details::operator_type o1 = expr_gen.get_operator(covov->f1()); + const details::operator_type o2 = operation; + + binary_functor_t f0 = covov->f0(); + binary_functor_t f1 = covov->f1(); + binary_functor_t f2 = reinterpret_cast(0); + + details::free_node(*(expr_gen.node_allocator_),branch[0]); + details::free_node(*(expr_gen.node_allocator_),branch[1]); + + expression_node_ptr result = error_node(); + + const bool synthesis_result = + synthesize_sf4ext_expression::template compile + (expr_gen, id(expr_gen, o0, o1, o2), c0, v0, v1, c1, result); + + if (synthesis_result) + return result; + else if (!expr_gen.valid_operator(o2,f2)) + return error_node(); + + exprtk_debug(("((c0 o0 (v0 o1 v1)) o2 c1)\n")); + + return node_type::allocate(*(expr_gen.node_allocator_), c0, v0, v1, c1, f0, f1, f2); + } + + static inline std::string id(expression_generator& expr_gen, + const details::operator_type o0, + const details::operator_type o1, + const details::operator_type o2) + { + return details::build_string() + << "(t" << expr_gen.to_str(o0) + << "(t" << expr_gen.to_str(o1) + << "t)" << expr_gen.to_str(o2) + << "t"; + } + }; + + struct synthesize_vococov_expression4 + { + typedef typename vococov_t::type4 node_type; + static inline expression_node_ptr process(expression_generator&, + const details::operator_type&, + expression_node_ptr (&)[2]) + { + // ((v0 o0 (c0 o1 c1)) o2 v1) - Not possible + exprtk_debug(("((v0 o0 (c0 o1 c1)) o2 v1) - Not possible\n")); + return error_node(); + } + + static inline std::string id(expression_generator&, + const details::operator_type, + const details::operator_type, + const details::operator_type) + { + return "INVALID"; + } + }; + #endif + + inline expression_node_ptr synthesize_uvouv_expression(const details::operator_type& operation, expression_node_ptr (&branch)[2]) + { + // Definition: uv o uv + details::operator_type o0 = static_cast*>(branch[0])->operation(); + details::operator_type o1 = static_cast*>(branch[1])->operation(); + const Type& v0 = static_cast*>(branch[0])->v(); + const Type& v1 = static_cast*>(branch[1])->v(); + unary_functor_t u0 = reinterpret_cast (0); + unary_functor_t u1 = reinterpret_cast (0); + binary_functor_t f = reinterpret_cast(0); + + if (!valid_operator(o0,u0)) + return error_node(); + else if (!valid_operator(o1,u1)) + return error_node(); + else if (!valid_operator(operation,f)) + return error_node(); + + expression_node_ptr result = error_node(); + + if ( + (details::e_neg == o0) && + (details::e_neg == o1) + ) + { + switch (operation) + { + // (-v0 + -v1) --> -(v0 + v1) + case details::e_add : result = (*this)(details::e_neg, + node_allocator_-> + allocate_rr > >(v0, v1)); + exprtk_debug(("(-v0 + -v1) --> -(v0 + v1)\n")); + break; + + // (-v0 - -v1) --> (v1 - v0) + case details::e_sub : result = node_allocator_-> + allocate_rr > >(v1, v0); + exprtk_debug(("(-v0 - -v1) --> (v1 - v0)\n")); + break; + + // (-v0 * -v1) --> (v0 * v1) + case details::e_mul : result = node_allocator_-> + allocate_rr > >(v0, v1); + exprtk_debug(("(-v0 * -v1) --> (v0 * v1)\n")); + break; + + // (-v0 / -v1) --> (v0 / v1) + case details::e_div : result = node_allocator_-> + allocate_rr > >(v0, v1); + exprtk_debug(("(-v0 / -v1) --> (v0 / v1)\n")); + break; + + default : break; + } + } + + if (0 == result) + { + result = node_allocator_-> + allocate_rrrrr >(v0, v1, u0, u1, f); + } + + details::free_all_nodes(*node_allocator_,branch); + return result; + } + + #undef basic_opr_switch_statements + #undef extended_opr_switch_statements + #undef unary_opr_switch_statements + + #ifndef exprtk_disable_string_capabilities + + #define string_opr_switch_statements \ + case_stmt(details::e_lt , details::lt_op ) \ + case_stmt(details::e_lte , details::lte_op ) \ + case_stmt(details::e_gt , details::gt_op ) \ + case_stmt(details::e_gte , details::gte_op ) \ + case_stmt(details::e_eq , details::eq_op ) \ + case_stmt(details::e_ne , details::ne_op ) \ + case_stmt(details::e_in , details::in_op ) \ + case_stmt(details::e_like , details::like_op ) \ + case_stmt(details::e_ilike , details::ilike_op) \ + + template + inline expression_node_ptr synthesize_str_xrox_expression_impl(const details::operator_type& opr, + T0 s0, T1 s1, + range_t rp0) + { + switch (opr) + { + #define case_stmt(op0, op1) \ + case op0 : return node_allocator_-> \ + allocate_ttt >,T0,T1> \ + (s0, s1, rp0); \ + + string_opr_switch_statements + #undef case_stmt + default : return error_node(); + } + } + + template + inline expression_node_ptr synthesize_str_xoxr_expression_impl(const details::operator_type& opr, + T0 s0, T1 s1, + range_t rp1) + { + switch (opr) + { + #define case_stmt(op0, op1) \ + case op0 : return node_allocator_-> \ + allocate_ttt >,T0,T1> \ + (s0, s1, rp1); \ + + string_opr_switch_statements + #undef case_stmt + default : return error_node(); + } + } + + template + inline expression_node_ptr synthesize_str_xroxr_expression_impl(const details::operator_type& opr, + T0 s0, T1 s1, + range_t rp0, range_t rp1) + { + switch (opr) + { + #define case_stmt(op0, op1) \ + case op0 : return node_allocator_-> \ + allocate_tttt >,T0,T1> \ + (s0, s1, rp0, rp1); \ + + string_opr_switch_statements + #undef case_stmt + default : return error_node(); + } + } + + template + inline expression_node_ptr synthesize_sos_expression_impl(const details::operator_type& opr, T0 s0, T1 s1) + { + switch (opr) + { + #define case_stmt(op0, op1) \ + case op0 : return node_allocator_-> \ + allocate_tt >,T0,T1>(s0, s1); \ + + string_opr_switch_statements + #undef case_stmt + default : return error_node(); + } + } + + inline expression_node_ptr synthesize_sos_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2]) + { + std::string& s0 = static_cast*>(branch[0])->ref(); + std::string& s1 = static_cast*>(branch[1])->ref(); + + return synthesize_sos_expression_impl(opr, s0, s1); + } + + inline expression_node_ptr synthesize_sros_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2]) + { + std::string& s0 = static_cast*>(branch[0])->ref (); + std::string& s1 = static_cast*> (branch[1])->ref (); + range_t rp0 = static_cast*>(branch[0])->range(); + + static_cast*>(branch[0])->range_ref().clear(); + + details::free_node(*node_allocator_,branch[0]); + + return synthesize_str_xrox_expression_impl(opr, s0, s1, rp0); + } + + inline expression_node_ptr synthesize_sosr_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2]) + { + std::string& s0 = static_cast*> (branch[0])->ref (); + std::string& s1 = static_cast*>(branch[1])->ref (); + range_t rp1 = static_cast*>(branch[1])->range(); + + static_cast*>(branch[1])->range_ref().clear(); + + details::free_node(*node_allocator_,branch[1]); + + return synthesize_str_xoxr_expression_impl(opr, s0, s1, rp1); + } + + inline expression_node_ptr synthesize_socsr_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2]) + { + std::string& s0 = static_cast*> (branch[0])->ref (); + std::string s1 = static_cast*>(branch[1])->str (); + range_t rp1 = static_cast*>(branch[1])->range(); + + static_cast*>(branch[1])->range_ref().clear(); + + details::free_node(*node_allocator_,branch[1]); + + return synthesize_str_xoxr_expression_impl(opr, s0, s1, rp1); + } + + inline expression_node_ptr synthesize_srosr_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2]) + { + std::string& s0 = static_cast*>(branch[0])->ref (); + std::string& s1 = static_cast*>(branch[1])->ref (); + range_t rp0 = static_cast*>(branch[0])->range(); + range_t rp1 = static_cast*>(branch[1])->range(); + + static_cast*>(branch[0])->range_ref().clear(); + static_cast*>(branch[1])->range_ref().clear(); + + details::free_node(*node_allocator_,branch[0]); + details::free_node(*node_allocator_,branch[1]); + + return synthesize_str_xroxr_expression_impl(opr, s0, s1, rp0, rp1); + } + + inline expression_node_ptr synthesize_socs_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2]) + { + std::string& s0 = static_cast< details::stringvar_node*>(branch[0])->ref(); + std::string s1 = static_cast*>(branch[1])->str(); + + details::free_node(*node_allocator_,branch[1]); + + return synthesize_sos_expression_impl(opr, s0, s1); + } + + inline expression_node_ptr synthesize_csos_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2]) + { + std::string s0 = static_cast*>(branch[0])->str(); + std::string& s1 = static_cast* >(branch[1])->ref(); + + details::free_node(*node_allocator_,branch[0]); + + return synthesize_sos_expression_impl(opr, s0, s1); + } + + inline expression_node_ptr synthesize_csosr_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2]) + { + std::string s0 = static_cast*>(branch[0])->str (); + std::string& s1 = static_cast* >(branch[1])->ref (); + range_t rp1 = static_cast* >(branch[1])->range(); + + static_cast*>(branch[1])->range_ref().clear(); + + details::free_node(*node_allocator_,branch[0]); + details::free_node(*node_allocator_,branch[1]); + + return synthesize_str_xoxr_expression_impl(opr, s0, s1, rp1); + } + + inline expression_node_ptr synthesize_srocs_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2]) + { + std::string& s0 = static_cast* >(branch[0])->ref (); + std::string s1 = static_cast*>(branch[1])->str (); + range_t rp0 = static_cast* >(branch[0])->range(); + + static_cast*>(branch[0])->range_ref().clear(); + + details::free_node(*node_allocator_,branch[0]); + details::free_node(*node_allocator_,branch[1]); + + return synthesize_str_xrox_expression_impl(opr, s0, s1, rp0); + } + + inline expression_node_ptr synthesize_srocsr_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2]) + { + std::string& s0 = static_cast* >(branch[0])->ref (); + std::string s1 = static_cast*>(branch[1])->str (); + range_t rp0 = static_cast* >(branch[0])->range(); + range_t rp1 = static_cast*>(branch[1])->range(); + + static_cast*> (branch[0])->range_ref().clear(); + static_cast*>(branch[1])->range_ref().clear(); + + details::free_node(*node_allocator_,branch[0]); + details::free_node(*node_allocator_,branch[1]); + + return synthesize_str_xroxr_expression_impl(opr, s0, s1, rp0, rp1); + } + + inline expression_node_ptr synthesize_csocs_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2]) + { + const std::string s0 = static_cast*>(branch[0])->str(); + const std::string s1 = static_cast*>(branch[1])->str(); + + expression_node_ptr result = error_node(); + + if (details::e_add == opr) + result = node_allocator_->allocate_c >(s0 + s1); + else if (details::e_in == opr) + result = node_allocator_->allocate_c >(details::in_op ::process(s0,s1)); + else if (details::e_like == opr) + result = node_allocator_->allocate_c >(details::like_op ::process(s0,s1)); + else if (details::e_ilike == opr) + result = node_allocator_->allocate_c >(details::ilike_op::process(s0,s1)); + else + { + expression_node_ptr temp = synthesize_sos_expression_impl(opr, s0, s1); + + const Type v = temp->value(); + + details::free_node(*node_allocator_,temp); + + result = node_allocator_->allocate(v); + } + + details::free_all_nodes(*node_allocator_,branch); + + return result; + } + + inline expression_node_ptr synthesize_csocsr_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2]) + { + const std::string s0 = static_cast* >(branch[0])->str (); + std::string s1 = static_cast*>(branch[1])->str (); + range_t rp1 = static_cast*>(branch[1])->range(); + + static_cast*>(branch[1])->range_ref().clear(); + + details::free_node(*node_allocator_,branch[0]); + details::free_node(*node_allocator_,branch[1]); + + return synthesize_str_xoxr_expression_impl(opr, s0, s1, rp1); + } + + inline expression_node_ptr synthesize_csros_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2]) + { + std::string s0 = static_cast*>(branch[0])->str (); + std::string& s1 = static_cast* >(branch[1])->ref (); + range_t rp0 = static_cast*>(branch[0])->range(); + + static_cast*>(branch[0])->range_ref().clear(); + + details::free_node(*node_allocator_,branch[0]); + + return synthesize_str_xrox_expression_impl(opr, s0, s1, rp0); + } + + inline expression_node_ptr synthesize_csrosr_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2]) + { + const std::string s0 = static_cast*>(branch[0])->str (); + std::string& s1 = static_cast* >(branch[1])->ref (); + const range_t rp0 = static_cast*>(branch[0])->range(); + const range_t rp1 = static_cast* >(branch[1])->range(); + + static_cast*>(branch[0])->range_ref().clear(); + static_cast*> (branch[1])->range_ref().clear(); + + details::free_node(*node_allocator_,branch[0]); + details::free_node(*node_allocator_,branch[1]); + + return synthesize_str_xroxr_expression_impl(opr, s0, s1, rp0, rp1); + } + + inline expression_node_ptr synthesize_csrocs_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2]) + { + const std::string s0 = static_cast*>(branch[0])->str (); + const std::string s1 = static_cast* >(branch[1])->str (); + const range_t rp0 = static_cast*>(branch[0])->range(); + + static_cast*>(branch[0])->range_ref().clear(); + + details::free_all_nodes(*node_allocator_,branch); + + return synthesize_str_xrox_expression_impl(opr, s0, s1, rp0); + } + + inline expression_node_ptr synthesize_csrocsr_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2]) + { + const std::string s0 = static_cast*>(branch[0])->str (); + const std::string s1 = static_cast*>(branch[1])->str (); + const range_t rp0 = static_cast*>(branch[0])->range(); + const range_t rp1 = static_cast*>(branch[1])->range(); + + static_cast*>(branch[0])->range_ref().clear(); + static_cast*>(branch[1])->range_ref().clear(); + + details::free_all_nodes(*node_allocator_,branch); + + return synthesize_str_xroxr_expression_impl(opr, s0, s1, rp0, rp1); + } + + inline expression_node_ptr synthesize_strogen_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2]) + { + switch (opr) + { + #define case_stmt(op0, op1) \ + case op0 : return node_allocator_-> \ + allocate_ttt > > \ + (opr, branch[0], branch[1]); \ + + string_opr_switch_statements + #undef case_stmt + default : return error_node(); + } + } + + #undef string_opr_switch_statements + #endif + + #ifndef exprtk_disable_string_capabilities + inline expression_node_ptr synthesize_string_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2]) + { + if ((0 == branch[0]) || (0 == branch[1])) + { + details::free_all_nodes(*node_allocator_,branch); + + return error_node(); + } + + const bool b0_is_s = details::is_string_node (branch[0]); + const bool b0_is_cs = details::is_const_string_node (branch[0]); + const bool b0_is_sr = details::is_string_range_node (branch[0]); + const bool b0_is_csr = details::is_const_string_range_node(branch[0]); + + const bool b1_is_s = details::is_string_node (branch[1]); + const bool b1_is_cs = details::is_const_string_node (branch[1]); + const bool b1_is_sr = details::is_string_range_node (branch[1]); + const bool b1_is_csr = details::is_const_string_range_node(branch[1]); + + const bool b0_is_gen = details::is_string_assignment_node (branch[0]) || + details::is_genricstring_range_node(branch[0]) || + details::is_string_concat_node (branch[0]) || + details::is_string_function_node (branch[0]) || + details::is_string_condition_node (branch[0]) || + details::is_string_ccondition_node (branch[0]) || + details::is_string_vararg_node (branch[0]) ; + + const bool b1_is_gen = details::is_string_assignment_node (branch[1]) || + details::is_genricstring_range_node(branch[1]) || + details::is_string_concat_node (branch[1]) || + details::is_string_function_node (branch[1]) || + details::is_string_condition_node (branch[1]) || + details::is_string_ccondition_node (branch[1]) || + details::is_string_vararg_node (branch[1]) ; + + if (details::e_add == opr) + { + if (!b0_is_cs || !b1_is_cs) + { + return synthesize_expression(opr,branch); + } + } + + if (b0_is_gen || b1_is_gen) + { + return synthesize_strogen_expression(opr,branch); + } + else if (b0_is_s) + { + if (b1_is_s ) return synthesize_sos_expression (opr,branch); + else if (b1_is_cs ) return synthesize_socs_expression (opr,branch); + else if (b1_is_sr ) return synthesize_sosr_expression (opr,branch); + else if (b1_is_csr) return synthesize_socsr_expression (opr,branch); + } + else if (b0_is_cs) + { + if (b1_is_s ) return synthesize_csos_expression (opr,branch); + else if (b1_is_cs ) return synthesize_csocs_expression (opr,branch); + else if (b1_is_sr ) return synthesize_csosr_expression (opr,branch); + else if (b1_is_csr) return synthesize_csocsr_expression(opr,branch); + } + else if (b0_is_sr) + { + if (b1_is_s ) return synthesize_sros_expression (opr,branch); + else if (b1_is_sr ) return synthesize_srosr_expression (opr,branch); + else if (b1_is_cs ) return synthesize_srocs_expression (opr,branch); + else if (b1_is_csr) return synthesize_srocsr_expression(opr,branch); + } + else if (b0_is_csr) + { + if (b1_is_s ) return synthesize_csros_expression (opr,branch); + else if (b1_is_sr ) return synthesize_csrosr_expression (opr,branch); + else if (b1_is_cs ) return synthesize_csrocs_expression (opr,branch); + else if (b1_is_csr) return synthesize_csrocsr_expression(opr,branch); + } + + return error_node(); + } + #else + inline expression_node_ptr synthesize_string_expression(const details::operator_type&, expression_node_ptr (&branch)[2]) + { + details::free_all_nodes(*node_allocator_,branch); + return error_node(); + } + #endif + + #ifndef exprtk_disable_string_capabilities + inline expression_node_ptr synthesize_string_expression(const details::operator_type& opr, expression_node_ptr (&branch)[3]) + { + if (details::e_inrange != opr) + return error_node(); + else if ((0 == branch[0]) || (0 == branch[1]) || (0 == branch[2])) + { + details::free_all_nodes(*node_allocator_,branch); + + return error_node(); + } + else if ( + details::is_const_string_node(branch[0]) && + details::is_const_string_node(branch[1]) && + details::is_const_string_node(branch[2]) + ) + { + const std::string s0 = static_cast*>(branch[0])->str(); + const std::string s1 = static_cast*>(branch[1])->str(); + const std::string s2 = static_cast*>(branch[2])->str(); + + const Type v = (((s0 <= s1) && (s1 <= s2)) ? Type(1) : Type(0)); + + details::free_all_nodes(*node_allocator_,branch); + + return node_allocator_->allocate_c >(v); + } + else if ( + details::is_string_node(branch[0]) && + details::is_string_node(branch[1]) && + details::is_string_node(branch[2]) + ) + { + std::string& s0 = static_cast*>(branch[0])->ref(); + std::string& s1 = static_cast*>(branch[1])->ref(); + std::string& s2 = static_cast*>(branch[2])->ref(); + + typedef typename details::sosos_node > inrange_t; + + return node_allocator_->allocate_type(s0, s1, s2); + } + else if ( + details::is_const_string_node(branch[0]) && + details::is_string_node(branch[1]) && + details::is_const_string_node(branch[2]) + ) + { + std::string s0 = static_cast*>(branch[0])->str(); + std::string& s1 = static_cast* >(branch[1])->ref(); + std::string s2 = static_cast*>(branch[2])->str(); + + typedef typename details::sosos_node > inrange_t; + + details::free_node(*node_allocator_,branch[0]); + details::free_node(*node_allocator_,branch[2]); + + return node_allocator_->allocate_type(s0, s1, s2); + } + else if ( + details::is_string_node(branch[0]) && + details::is_const_string_node(branch[1]) && + details::is_string_node(branch[2]) + ) + { + std::string& s0 = static_cast* >(branch[0])->ref(); + std::string s1 = static_cast*>(branch[1])->str(); + std::string& s2 = static_cast* >(branch[2])->ref(); + + typedef typename details::sosos_node > inrange_t; + + details::free_node(*node_allocator_,branch[1]); + + return node_allocator_->allocate_type(s0, s1, s2); + } + else if ( + details::is_string_node(branch[0]) && + details::is_string_node(branch[1]) && + details::is_const_string_node(branch[2]) + ) + { + std::string& s0 = static_cast* >(branch[0])->ref(); + std::string& s1 = static_cast* >(branch[1])->ref(); + std::string s2 = static_cast*>(branch[2])->str(); + + typedef typename details::sosos_node > inrange_t; + + details::free_node(*node_allocator_,branch[2]); + + return node_allocator_->allocate_type(s0, s1, s2); + } + else if ( + details::is_const_string_node(branch[0]) && + details:: is_string_node(branch[1]) && + details:: is_string_node(branch[2]) + ) + { + std::string s0 = static_cast*>(branch[0])->str(); + std::string& s1 = static_cast* >(branch[1])->ref(); + std::string& s2 = static_cast* >(branch[2])->ref(); + + typedef typename details::sosos_node > inrange_t; + + details::free_node(*node_allocator_,branch[0]); + + return node_allocator_->allocate_type(s0, s1, s2); + } + else + return error_node(); + } + #else + inline expression_node_ptr synthesize_string_expression(const details::operator_type&, expression_node_ptr (&branch)[3]) + { + details::free_all_nodes(*node_allocator_,branch); + return error_node(); + } + #endif + + inline expression_node_ptr synthesize_null_expression(const details::operator_type& operation, expression_node_ptr (&branch)[2]) + { + /* + Note: The following are the type promotion rules + that relate to operations that include 'null': + 0. null ==/!= null --> true false + 1. null operation null --> null + 2. x ==/!= null --> true/false + 3. null ==/!= x --> true/false + 4. x operation null --> x + 5. null operation x --> x + */ + + typedef typename details::null_eq_node nulleq_node_t; + + const bool b0_null = details::is_null_node(branch[0]); + const bool b1_null = details::is_null_node(branch[1]); + + if (b0_null && b1_null) + { + expression_node_ptr result = error_node(); + + if (details::e_eq == operation) + result = node_allocator_->allocate_c(T(1)); + else if (details::e_ne == operation) + result = node_allocator_->allocate_c(T(0)); + + if (result) + { + details::free_node(*node_allocator_,branch[0]); + details::free_node(*node_allocator_,branch[1]); + + return result; + } + + details::free_node(*node_allocator_,branch[1]); + + return branch[0]; + } + else if (details::e_eq == operation) + { + expression_node_ptr result = node_allocator_-> + allocate_rc(branch[b0_null ? 0 : 1],true); + + details::free_node(*node_allocator_,branch[b0_null ? 1 : 0]); + + return result; + } + else if (details::e_ne == operation) + { + expression_node_ptr result = node_allocator_-> + allocate_rc(branch[b0_null ? 0 : 1],false); + + details::free_node(*node_allocator_,branch[b0_null ? 1 : 0]); + + return result; + } + else if (b0_null) + { + details::free_node(*node_allocator_,branch[0]); + branch[0] = branch[1]; + branch[1] = error_node(); + } + else if (b1_null) + { + details::free_node(*node_allocator_,branch[1]); + branch[1] = error_node(); + } + + if ( + (details::e_add == operation) || (details::e_sub == operation) || + (details::e_mul == operation) || (details::e_div == operation) || + (details::e_mod == operation) || (details::e_pow == operation) + ) + { + return branch[0]; + } + + details::free_node(*node_allocator_, branch[0]); + + if ( + (details::e_lt == operation) || (details::e_lte == operation) || + (details::e_gt == operation) || (details::e_gte == operation) || + (details::e_and == operation) || (details::e_nand == operation) || + (details::e_or == operation) || (details::e_nor == operation) || + (details::e_xor == operation) || (details::e_xnor == operation) || + (details::e_in == operation) || (details::e_like == operation) || + (details::e_ilike == operation) + ) + { + return node_allocator_->allocate_c(T(0)); + } + + return node_allocator_->allocate >(); + } + + template + inline expression_node_ptr synthesize_expression(const details::operator_type& operation, expression_node_ptr (&branch)[N]) + { + if ( + (details::e_in == operation) || + (details::e_like == operation) || + (details::e_ilike == operation) + ) + { + free_all_nodes(*node_allocator_,branch); + + return error_node(); + } + else if (!details::all_nodes_valid(branch)) + { + free_all_nodes(*node_allocator_,branch); + + return error_node(); + } + else if ((details::e_default != operation)) + { + // Attempt simple constant folding optimisation. + expression_node_ptr expression_point = node_allocator_->allocate(operation,branch); + + if (is_constant_foldable(branch)) + { + const Type v = expression_point->value(); + details::free_node(*node_allocator_,expression_point); + + return node_allocator_->allocate(v); + } + + if (expression_point && expression_point->valid()) + { + return expression_point; + } + + parser_->set_error(parser_error::make_error( + parser_error::e_parser, + token_t(), + "ERR281 - Failed to synthesize node: NodeType", + exprtk_error_location)); + + details::free_node(*node_allocator_, expression_point); + } + + return error_node(); + } + + template + inline expression_node_ptr synthesize_expression(F* f, expression_node_ptr (&branch)[N]) + { + if (!details::all_nodes_valid(branch)) + { + free_all_nodes(*node_allocator_,branch); + + return error_node(); + } + + typedef typename details::function_N_node function_N_node_t; + + // Attempt simple constant folding optimisation. + + expression_node_ptr expression_point = node_allocator_->allocate(f); + function_N_node_t* func_node_ptr = dynamic_cast(expression_point); + + if (0 == func_node_ptr) + { + free_all_nodes(*node_allocator_,branch); + + return error_node(); + } + else + func_node_ptr->init_branches(branch); + + if (is_constant_foldable(branch) && !f->has_side_effects()) + { + Type v = expression_point->value(); + details::free_node(*node_allocator_,expression_point); + + return node_allocator_->allocate(v); + } + + parser_->state_.activate_side_effect("synthesize_expression(function)"); + + return expression_point; + } + + bool strength_reduction_enabled_; + details::node_allocator* node_allocator_; + synthesize_map_t synthesize_map_; + unary_op_map_t* unary_op_map_; + binary_op_map_t* binary_op_map_; + inv_binary_op_map_t* inv_binary_op_map_; + sf3_map_t* sf3_map_; + sf4_map_t* sf4_map_; + parser_t* parser_; + }; // class expression_generator + + inline void set_error(const parser_error::type& error_type) + { + error_list_.push_back(error_type); + } + + inline void remove_last_error() + { + if (!error_list_.empty()) + { + error_list_.pop_back(); + } + } + + inline void set_synthesis_error(const std::string& synthesis_error_message) + { + if (synthesis_error_.empty()) + { + synthesis_error_ = synthesis_error_message; + } + } + + inline void register_local_vars(expression& e) + { + for (std::size_t i = 0; i < sem_.size(); ++i) + { + scope_element& se = sem_.get_element(i); + + exprtk_debug(("register_local_vars() - se[%s]\n", se.name.c_str())); + + if ( + (scope_element::e_variable == se.type) || + (scope_element::e_literal == se.type) || + (scope_element::e_vecelem == se.type) + ) + { + if (se.var_node) + { + e.register_local_var(se.var_node); + } + + if (se.data) + { + e.register_local_data(se.data, 1, 0); + } + } + else if (scope_element::e_vector == se.type) + { + if (se.vec_node) + { + e.register_local_var(se.vec_node); + } + + if (se.data) + { + e.register_local_data(se.data, se.size, 1); + } + } + #ifndef exprtk_disable_string_capabilities + else if (scope_element::e_string == se.type) + { + if (se.str_node) + { + e.register_local_var(se.str_node); + } + + if (se.data) + { + e.register_local_data(se.data, se.size, 2); + } + } + #endif + + se.var_node = 0; + se.vec_node = 0; + #ifndef exprtk_disable_string_capabilities + se.str_node = 0; + #endif + se.data = 0; + se.ref_count = 0; + se.active = false; + } + } + + inline void register_return_results(expression& e) + { + e.register_return_results(results_context_); + results_context_ = 0; + } + + inline void load_unary_operations_map(unary_op_map_t& m) + { + #define register_unary_op(Op, UnaryFunctor) \ + m.insert(std::make_pair(Op,UnaryFunctor::process)); \ + + register_unary_op(details::e_abs , details::abs_op ) + register_unary_op(details::e_acos , details::acos_op ) + register_unary_op(details::e_acosh , details::acosh_op) + register_unary_op(details::e_asin , details::asin_op ) + register_unary_op(details::e_asinh , details::asinh_op) + register_unary_op(details::e_atanh , details::atanh_op) + register_unary_op(details::e_ceil , details::ceil_op ) + register_unary_op(details::e_cos , details::cos_op ) + register_unary_op(details::e_cosh , details::cosh_op ) + register_unary_op(details::e_exp , details::exp_op ) + register_unary_op(details::e_expm1 , details::expm1_op) + register_unary_op(details::e_floor , details::floor_op) + register_unary_op(details::e_log , details::log_op ) + register_unary_op(details::e_log10 , details::log10_op) + register_unary_op(details::e_log2 , details::log2_op ) + register_unary_op(details::e_log1p , details::log1p_op) + register_unary_op(details::e_neg , details::neg_op ) + register_unary_op(details::e_pos , details::pos_op ) + register_unary_op(details::e_round , details::round_op) + register_unary_op(details::e_sin , details::sin_op ) + register_unary_op(details::e_sinc , details::sinc_op ) + register_unary_op(details::e_sinh , details::sinh_op ) + register_unary_op(details::e_sqrt , details::sqrt_op ) + register_unary_op(details::e_tan , details::tan_op ) + register_unary_op(details::e_tanh , details::tanh_op ) + register_unary_op(details::e_cot , details::cot_op ) + register_unary_op(details::e_sec , details::sec_op ) + register_unary_op(details::e_csc , details::csc_op ) + register_unary_op(details::e_r2d , details::r2d_op ) + register_unary_op(details::e_d2r , details::d2r_op ) + register_unary_op(details::e_d2g , details::d2g_op ) + register_unary_op(details::e_g2d , details::g2d_op ) + register_unary_op(details::e_notl , details::notl_op ) + register_unary_op(details::e_sgn , details::sgn_op ) + register_unary_op(details::e_erf , details::erf_op ) + register_unary_op(details::e_erfc , details::erfc_op ) + register_unary_op(details::e_ncdf , details::ncdf_op ) + register_unary_op(details::e_frac , details::frac_op ) + register_unary_op(details::e_trunc , details::trunc_op) + #undef register_unary_op + } + + inline void load_binary_operations_map(binary_op_map_t& m) + { + typedef typename binary_op_map_t::value_type value_type; + + #define register_binary_op(Op, BinaryFunctor) \ + m.insert(value_type(Op,BinaryFunctor::process)); \ + + register_binary_op(details::e_add , details::add_op ) + register_binary_op(details::e_sub , details::sub_op ) + register_binary_op(details::e_mul , details::mul_op ) + register_binary_op(details::e_div , details::div_op ) + register_binary_op(details::e_mod , details::mod_op ) + register_binary_op(details::e_pow , details::pow_op ) + register_binary_op(details::e_lt , details::lt_op ) + register_binary_op(details::e_lte , details::lte_op ) + register_binary_op(details::e_gt , details::gt_op ) + register_binary_op(details::e_gte , details::gte_op ) + register_binary_op(details::e_eq , details::eq_op ) + register_binary_op(details::e_ne , details::ne_op ) + register_binary_op(details::e_and , details::and_op ) + register_binary_op(details::e_nand , details::nand_op) + register_binary_op(details::e_or , details::or_op ) + register_binary_op(details::e_nor , details::nor_op ) + register_binary_op(details::e_xor , details::xor_op ) + register_binary_op(details::e_xnor , details::xnor_op) + #undef register_binary_op + } + + inline void load_inv_binary_operations_map(inv_binary_op_map_t& m) + { + typedef typename inv_binary_op_map_t::value_type value_type; + + #define register_binary_op(Op, BinaryFunctor) \ + m.insert(value_type(BinaryFunctor::process,Op)); \ + + register_binary_op(details::e_add , details::add_op ) + register_binary_op(details::e_sub , details::sub_op ) + register_binary_op(details::e_mul , details::mul_op ) + register_binary_op(details::e_div , details::div_op ) + register_binary_op(details::e_mod , details::mod_op ) + register_binary_op(details::e_pow , details::pow_op ) + register_binary_op(details::e_lt , details::lt_op ) + register_binary_op(details::e_lte , details::lte_op ) + register_binary_op(details::e_gt , details::gt_op ) + register_binary_op(details::e_gte , details::gte_op ) + register_binary_op(details::e_eq , details::eq_op ) + register_binary_op(details::e_ne , details::ne_op ) + register_binary_op(details::e_and , details::and_op ) + register_binary_op(details::e_nand , details::nand_op) + register_binary_op(details::e_or , details::or_op ) + register_binary_op(details::e_nor , details::nor_op ) + register_binary_op(details::e_xor , details::xor_op ) + register_binary_op(details::e_xnor , details::xnor_op) + #undef register_binary_op + } + + inline void load_sf3_map(sf3_map_t& sf3_map) + { + typedef std::pair pair_t; + + #define register_sf3(Op) \ + sf3_map[details::sf##Op##_op::id()] = pair_t(details::sf##Op##_op::process,details::e_sf##Op); \ + + register_sf3(00) register_sf3(01) register_sf3(02) register_sf3(03) + register_sf3(04) register_sf3(05) register_sf3(06) register_sf3(07) + register_sf3(08) register_sf3(09) register_sf3(10) register_sf3(11) + register_sf3(12) register_sf3(13) register_sf3(14) register_sf3(15) + register_sf3(16) register_sf3(17) register_sf3(18) register_sf3(19) + register_sf3(20) register_sf3(21) register_sf3(22) register_sf3(23) + register_sf3(24) register_sf3(25) register_sf3(26) register_sf3(27) + register_sf3(28) register_sf3(29) register_sf3(30) + #undef register_sf3 + + #define register_sf3_extid(Id, Op) \ + sf3_map[Id] = pair_t(details::sf##Op##_op::process,details::e_sf##Op); \ + + register_sf3_extid("(t-t)-t",23) // (t-t)-t --> t-(t+t) + #undef register_sf3_extid + } + + inline void load_sf4_map(sf4_map_t& sf4_map) + { + typedef std::pair pair_t; + + #define register_sf4(Op) \ + sf4_map[details::sf##Op##_op::id()] = pair_t(details::sf##Op##_op::process,details::e_sf##Op); \ + + register_sf4(48) register_sf4(49) register_sf4(50) register_sf4(51) + register_sf4(52) register_sf4(53) register_sf4(54) register_sf4(55) + register_sf4(56) register_sf4(57) register_sf4(58) register_sf4(59) + register_sf4(60) register_sf4(61) register_sf4(62) register_sf4(63) + register_sf4(64) register_sf4(65) register_sf4(66) register_sf4(67) + register_sf4(68) register_sf4(69) register_sf4(70) register_sf4(71) + register_sf4(72) register_sf4(73) register_sf4(74) register_sf4(75) + register_sf4(76) register_sf4(77) register_sf4(78) register_sf4(79) + register_sf4(80) register_sf4(81) register_sf4(82) register_sf4(83) + #undef register_sf4 + + #define register_sf4ext(Op) \ + sf4_map[details::sfext##Op##_op::id()] = pair_t(details::sfext##Op##_op::process,details::e_sf4ext##Op); \ + + register_sf4ext(00) register_sf4ext(01) register_sf4ext(02) register_sf4ext(03) + register_sf4ext(04) register_sf4ext(05) register_sf4ext(06) register_sf4ext(07) + register_sf4ext(08) register_sf4ext(09) register_sf4ext(10) register_sf4ext(11) + register_sf4ext(12) register_sf4ext(13) register_sf4ext(14) register_sf4ext(15) + register_sf4ext(16) register_sf4ext(17) register_sf4ext(18) register_sf4ext(19) + register_sf4ext(20) register_sf4ext(21) register_sf4ext(22) register_sf4ext(23) + register_sf4ext(24) register_sf4ext(25) register_sf4ext(26) register_sf4ext(27) + register_sf4ext(28) register_sf4ext(29) register_sf4ext(30) register_sf4ext(31) + register_sf4ext(32) register_sf4ext(33) register_sf4ext(34) register_sf4ext(35) + register_sf4ext(36) register_sf4ext(36) register_sf4ext(38) register_sf4ext(39) + register_sf4ext(40) register_sf4ext(41) register_sf4ext(42) register_sf4ext(43) + register_sf4ext(44) register_sf4ext(45) register_sf4ext(46) register_sf4ext(47) + register_sf4ext(48) register_sf4ext(49) register_sf4ext(50) register_sf4ext(51) + register_sf4ext(52) register_sf4ext(53) register_sf4ext(54) register_sf4ext(55) + register_sf4ext(56) register_sf4ext(57) register_sf4ext(58) register_sf4ext(59) + register_sf4ext(60) register_sf4ext(61) + #undef register_sf4ext + } + + inline results_context_t& results_ctx() + { + if (0 == results_context_) + { + results_context_ = new results_context_t(); + } + + return (*results_context_); + } + + inline void return_cleanup() + { + #ifndef exprtk_disable_return_statement + if (results_context_) + { + delete results_context_; + results_context_ = 0; + } + + state_.return_stmt_present = false; + #endif + } + + inline bool valid_settings() + { + const std::size_t max_local_vector_size_bytes = sizeof(T) * settings_.max_local_vector_size(); + + if (max_local_vector_size_bytes > settings_.max_total_local_symbol_size_bytes()) + { + set_error(make_error( + parser_error::e_parser, + "ERR282 - Max local vector size of " + details::to_str(max_local_vector_size_bytes) + " bytes " + "is larger than max total local symbol size of " + details::to_str(settings_.max_total_local_symbol_size_bytes()) + " bytes", + exprtk_error_location)); + + return false; + } + + return true; + } + + private: + + parser(const parser&) exprtk_delete; + parser& operator=(const parser&) exprtk_delete; + + settings_store settings_; + expression_generator expression_generator_; + details::node_allocator node_allocator_; + symtab_store symtab_store_; + dependent_entity_collector dec_; + std::deque error_list_; + std::deque brkcnt_list_; + parser_state state_; + bool resolve_unknown_symbol_; + results_context_t* results_context_; + unknown_symbol_resolver* unknown_symbol_resolver_; + unknown_symbol_resolver default_usr_; + base_ops_map_t base_ops_map_; + unary_op_map_t unary_op_map_; + binary_op_map_t binary_op_map_; + inv_binary_op_map_t inv_binary_op_map_; + sf3_map_t sf3_map_; + sf4_map_t sf4_map_; + std::string synthesis_error_; + scope_element_manager sem_; + std::vector current_state_stack_; + + immutable_memory_map_t immutable_memory_map_; + immutable_symtok_map_t immutable_symtok_map_; + + lexer::helper::helper_assembly helper_assembly_; + + lexer::helper::commutative_inserter commutative_inserter_; + lexer::helper::operator_joiner operator_joiner_2_; + lexer::helper::operator_joiner operator_joiner_3_; + lexer::helper::symbol_replacer symbol_replacer_; + lexer::helper::bracket_checker bracket_checker_; + lexer::helper::numeric_checker numeric_checker_; + lexer::helper::sequence_validator sequence_validator_; + lexer::helper::sequence_validator_3tokens sequence_validator_3tkns_; + + loop_runtime_check_ptr loop_runtime_check_; + vector_access_runtime_check_ptr vector_access_runtime_check_; + compilation_check_ptr compilation_check_ptr_; + assert_check_ptr assert_check_; + std::set assert_ids_; + + template + friend void details::disable_type_checking(ParserType& p); + }; // class parser + + namespace details + { + template + struct collector_helper + { + typedef exprtk::symbol_table symbol_table_t; + typedef exprtk::expression expression_t; + typedef exprtk::parser parser_t; + typedef typename parser_t::dependent_entity_collector::symbol_t symbol_t; + typedef typename parser_t::unknown_symbol_resolver usr_t; + + struct resolve_as_vector : public usr_t + { + typedef exprtk::parser parser_t; + + using usr_t::process; + + resolve_as_vector() + : usr_t(usr_t::e_usrmode_extended) + {} + + virtual bool process(const std::string& unknown_symbol, + symbol_table_t& symbol_table, + std::string&) exprtk_override + { + static T v[1]; + symbol_table.add_vector(unknown_symbol,v); + return true; + } + }; + + static inline bool collection_pass(const std::string& expression_string, + std::set& symbol_set, + const bool collect_variables, + const bool collect_functions, + const bool vector_pass, + symbol_table_t& ext_symbol_table) + { + symbol_table_t symbol_table; + expression_t expression; + parser_t parser; + + resolve_as_vector vect_resolver; + + expression.register_symbol_table(symbol_table ); + expression.register_symbol_table(ext_symbol_table); + + if (vector_pass) + parser.enable_unknown_symbol_resolver(&vect_resolver); + else + parser.enable_unknown_symbol_resolver(); + + if (collect_variables) + parser.dec().collect_variables() = true; + + if (collect_functions) + parser.dec().collect_functions() = true; + + bool pass_result = false; + + details::disable_type_checking(parser); + + if (parser.compile(expression_string, expression)) + { + pass_result = true; + + std::deque symb_list; + parser.dec().symbols(symb_list); + + for (std::size_t i = 0; i < symb_list.size(); ++i) + { + symbol_set.insert(symb_list[i].first); + } + } + + return pass_result; + } + }; + } + + template class Sequence> + inline bool collect_variables(const std::string& expression, + Sequence& symbol_list) + { + typedef double T; + typedef details::collector_helper collect_t; + + collect_t::symbol_table_t null_symbol_table; + + std::set symbol_set; + + const bool variable_pass = collect_t::collection_pass + (expression, symbol_set, true, false, false, null_symbol_table); + const bool vector_pass = collect_t::collection_pass + (expression, symbol_set, true, false, true, null_symbol_table); + + if (!variable_pass && !vector_pass) + return false; + + std::set::iterator itr = symbol_set.begin(); + + while (symbol_set.end() != itr) + { + symbol_list.push_back(*itr); + ++itr; + } + + return true; + } + + template class Sequence> + inline bool collect_variables(const std::string& expression, + exprtk::symbol_table& extrnl_symbol_table, + Sequence& symbol_list) + { + typedef details::collector_helper collect_t; + + std::set symbol_set; + + const bool variable_pass = collect_t::collection_pass + (expression, symbol_set, true, false, false, extrnl_symbol_table); + const bool vector_pass = collect_t::collection_pass + (expression, symbol_set, true, false, true, extrnl_symbol_table); + + if (!variable_pass && !vector_pass) + return false; + + std::set::iterator itr = symbol_set.begin(); + + while (symbol_set.end() != itr) + { + symbol_list.push_back(*itr); + ++itr; + } + + return true; + } + + template class Sequence> + inline bool collect_functions(const std::string& expression, + Sequence& symbol_list) + { + typedef double T; + typedef details::collector_helper collect_t; + + collect_t::symbol_table_t null_symbol_table; + + std::set symbol_set; + + const bool variable_pass = collect_t::collection_pass + (expression, symbol_set, false, true, false, null_symbol_table); + const bool vector_pass = collect_t::collection_pass + (expression, symbol_set, false, true, true, null_symbol_table); + + if (!variable_pass && !vector_pass) + return false; + + std::set::iterator itr = symbol_set.begin(); + + while (symbol_set.end() != itr) + { + symbol_list.push_back(*itr); + ++itr; + } + + return true; + } + + template class Sequence> + inline bool collect_functions(const std::string& expression, + exprtk::symbol_table& extrnl_symbol_table, + Sequence& symbol_list) + { + typedef details::collector_helper collect_t; + + std::set symbol_set; + + const bool variable_pass = collect_t::collection_pass + (expression, symbol_set, false, true, false, extrnl_symbol_table); + const bool vector_pass = collect_t::collection_pass + (expression, symbol_set, false, true, true, extrnl_symbol_table); + + if (!variable_pass && !vector_pass) + return false; + + std::set::iterator itr = symbol_set.begin(); + + while (symbol_set.end() != itr) + { + symbol_list.push_back(*itr); + ++itr; + } + + return true; + } + + template + inline T integrate(const expression& e, + T& x, + const T& r0, const T& r1, + const std::size_t number_of_intervals = 1000000) + { + if (r0 > r1) + return T(0); + + const T h = (r1 - r0) / (T(2) * number_of_intervals); + T total_area = T(0); + + for (std::size_t i = 0; i < number_of_intervals; ++i) + { + x = r0 + T(2) * i * h; + const T y0 = e.value(); x += h; + const T y1 = e.value(); x += h; + const T y2 = e.value(); x += h; + total_area += h * (y0 + T(4) * y1 + y2) / T(3); + } + + return total_area; + } + + template + inline T integrate(const expression& e, + const std::string& variable_name, + const T& r0, const T& r1, + const std::size_t number_of_intervals = 1000000) + { + const symbol_table& sym_table = e.get_symbol_table(); + + if (!sym_table.valid()) + { + return std::numeric_limits::quiet_NaN(); + } + + details::variable_node* var = sym_table.get_variable(variable_name); + + if (var) + { + T& x = var->ref(); + const T x_original = x; + const T result = integrate(e, x, r0, r1, number_of_intervals); + x = x_original; + + return result; + } + + return std::numeric_limits::quiet_NaN(); + } + + template + inline T derivative(const expression& e, + T& x, + const T& h = T(0.00000001)) + { + const T x_init = x; + const T _2h = T(2) * h; + + x = x_init + _2h; + const T y0 = e.value(); + x = x_init + h; + const T y1 = e.value(); + x = x_init - h; + const T y2 = e.value(); + x = x_init - _2h; + const T y3 = e.value(); + x = x_init; + + return (-y0 + T(8) * (y1 - y2) + y3) / (T(12) * h); + } + + template + inline T second_derivative(const expression& e, + T& x, + const T& h = T(0.00001)) + { + const T x_init = x; + const T _2h = T(2) * h; + + const T y = e.value(); + x = x_init + _2h; + const T y0 = e.value(); + x = x_init + h; + const T y1 = e.value(); + x = x_init - h; + const T y2 = e.value(); + x = x_init - _2h; + const T y3 = e.value(); + x = x_init; + + return (-y0 + T(16) * (y1 + y2) - T(30) * y - y3) / (T(12) * h * h); + } + + template + inline T third_derivative(const expression& e, + T& x, + const T& h = T(0.0001)) + { + const T x_init = x; + const T _2h = T(2) * h; + + x = x_init + _2h; + const T y0 = e.value(); + x = x_init + h; + const T y1 = e.value(); + x = x_init - h; + const T y2 = e.value(); + x = x_init - _2h; + const T y3 = e.value(); + x = x_init; + + return (y0 + T(2) * (y2 - y1) - y3) / (T(2) * h * h * h); + } + + template + inline T derivative(const expression& e, + const std::string& variable_name, + const T& h = T(0.00000001)) + { + const symbol_table& sym_table = e.get_symbol_table(); + + if (!sym_table.valid()) + { + return std::numeric_limits::quiet_NaN(); + } + + details::variable_node* var = sym_table.get_variable(variable_name); + + if (var) + { + T& x = var->ref(); + const T x_original = x; + const T result = derivative(e, x, h); + x = x_original; + + return result; + } + + return std::numeric_limits::quiet_NaN(); + } + + template + inline T second_derivative(const expression& e, + const std::string& variable_name, + const T& h = T(0.00001)) + { + const symbol_table& sym_table = e.get_symbol_table(); + + if (!sym_table.valid()) + { + return std::numeric_limits::quiet_NaN(); + } + + details::variable_node* var = sym_table.get_variable(variable_name); + + if (var) + { + T& x = var->ref(); + const T x_original = x; + const T result = second_derivative(e, x, h); + x = x_original; + + return result; + } + + return std::numeric_limits::quiet_NaN(); + } + + template + inline T third_derivative(const expression& e, + const std::string& variable_name, + const T& h = T(0.0001)) + { + const symbol_table& sym_table = e.get_symbol_table(); + + if (!sym_table.valid()) + { + return std::numeric_limits::quiet_NaN(); + } + + details::variable_node* var = sym_table.get_variable(variable_name); + + if (var) + { + T& x = var->ref(); + const T x_original = x; + const T result = third_derivative(e, x, h); + x = x_original; + + return result; + } + + return std::numeric_limits::quiet_NaN(); + } + + /* + Note: The following 'compute' routines are simple helpers, + for quickly setting up the required pieces of code in order + to evaluate an expression. By virtue of how they operate + there will be an overhead with regards to their setup and + teardown and hence should not be used in time critical + sections of code. + Furthermore they only assume a small sub set of variables, + no string variables or user defined functions. + */ + template + inline bool compute(const std::string& expression_string, T& result) + { + // No variables + symbol_table symbol_table; + symbol_table.add_constants(); + + expression expression; + expression.register_symbol_table(symbol_table); + + parser parser; + + if (parser.compile(expression_string,expression)) + { + result = expression.value(); + + return true; + } + else + return false; + } + + template + inline bool compute(const std::string& expression_string, + const T& x, + T& result) + { + // Only 'x' + static const std::string x_var("x"); + + symbol_table symbol_table; + symbol_table.add_constants(); + symbol_table.add_constant(x_var,x); + + expression expression; + expression.register_symbol_table(symbol_table); + + parser parser; + + if (parser.compile(expression_string,expression)) + { + result = expression.value(); + + return true; + } + else + return false; + } + + template + inline bool compute(const std::string& expression_string, + const T&x, const T& y, + T& result) + { + // Only 'x' and 'y' + static const std::string x_var("x"); + static const std::string y_var("y"); + + symbol_table symbol_table; + symbol_table.add_constants(); + symbol_table.add_constant(x_var,x); + symbol_table.add_constant(y_var,y); + + expression expression; + expression.register_symbol_table(symbol_table); + + parser parser; + + if (parser.compile(expression_string,expression)) + { + result = expression.value(); + + return true; + } + else + return false; + } + + template + inline bool compute(const std::string& expression_string, + const T& x, const T& y, const T& z, + T& result) + { + // Only 'x', 'y' or 'z' + static const std::string x_var("x"); + static const std::string y_var("y"); + static const std::string z_var("z"); + + symbol_table symbol_table; + symbol_table.add_constants(); + symbol_table.add_constant(x_var,x); + symbol_table.add_constant(y_var,y); + symbol_table.add_constant(z_var,z); + + expression expression; + expression.register_symbol_table(symbol_table); + + parser parser; + + if (parser.compile(expression_string,expression)) + { + result = expression.value(); + + return true; + } + else + return false; + } + + template + class polynomial : public ifunction + { + private: + + template + struct poly_impl { }; + + template + struct poly_impl + { + static inline T evaluate(const Type x, + const Type c12, const Type c11, const Type c10, const Type c9, const Type c8, + const Type c7, const Type c6, const Type c5, const Type c4, const Type c3, + const Type c2, const Type c1, const Type c0) + { + // p(x) = c_12x^12 + c_11x^11 + c_10x^10 + c_9x^9 + c_8x^8 + c_7x^7 + c_6x^6 + c_5x^5 + c_4x^4 + c_3x^3 + c_2x^2 + c_1x^1 + c_0x^0 + return ((((((((((((c12 * x + c11) * x + c10) * x + c9) * x + c8) * x + c7) * x + c6) * x + c5) * x + c4) * x + c3) * x + c2) * x + c1) * x + c0); + } + }; + + template + struct poly_impl + { + static inline T evaluate(const Type x, + const Type c11, const Type c10, const Type c9, const Type c8, const Type c7, + const Type c6, const Type c5, const Type c4, const Type c3, const Type c2, + const Type c1, const Type c0) + { + // p(x) = c_11x^11 + c_10x^10 + c_9x^9 + c_8x^8 + c_7x^7 + c_6x^6 + c_5x^5 + c_4x^4 + c_3x^3 + c_2x^2 + c_1x^1 + c_0x^0 + return (((((((((((c11 * x + c10) * x + c9) * x + c8) * x + c7) * x + c6) * x + c5) * x + c4) * x + c3) * x + c2) * x + c1) * x + c0); + } + }; + + template + struct poly_impl + { + static inline T evaluate(const Type x, + const Type c10, const Type c9, const Type c8, const Type c7, const Type c6, + const Type c5, const Type c4, const Type c3, const Type c2, const Type c1, + const Type c0) + { + // p(x) = c_10x^10 + c_9x^9 + c_8x^8 + c_7x^7 + c_6x^6 + c_5x^5 + c_4x^4 + c_3x^3 + c_2x^2 + c_1x^1 + c_0x^0 + return ((((((((((c10 * x + c9) * x + c8) * x + c7) * x + c6) * x + c5) * x + c4) * x + c3) * x + c2) * x + c1) * x + c0); + } + }; + + template + struct poly_impl + { + static inline T evaluate(const Type x, + const Type c9, const Type c8, const Type c7, const Type c6, const Type c5, + const Type c4, const Type c3, const Type c2, const Type c1, const Type c0) + { + // p(x) = c_9x^9 + c_8x^8 + c_7x^7 + c_6x^6 + c_5x^5 + c_4x^4 + c_3x^3 + c_2x^2 + c_1x^1 + c_0x^0 + return (((((((((c9 * x + c8) * x + c7) * x + c6) * x + c5) * x + c4) * x + c3) * x + c2) * x + c1) * x + c0); + } + }; + + template + struct poly_impl + { + static inline T evaluate(const Type x, + const Type c8, const Type c7, const Type c6, const Type c5, const Type c4, + const Type c3, const Type c2, const Type c1, const Type c0) + { + // p(x) = c_8x^8 + c_7x^7 + c_6x^6 + c_5x^5 + c_4x^4 + c_3x^3 + c_2x^2 + c_1x^1 + c_0x^0 + return ((((((((c8 * x + c7) * x + c6) * x + c5) * x + c4) * x + c3) * x + c2) * x + c1) * x + c0); + } + }; + + template + struct poly_impl + { + static inline T evaluate(const Type x, + const Type c7, const Type c6, const Type c5, const Type c4, const Type c3, + const Type c2, const Type c1, const Type c0) + { + // p(x) = c_7x^7 + c_6x^6 + c_5x^5 + c_4x^4 + c_3x^3 + c_2x^2 + c_1x^1 + c_0x^0 + return (((((((c7 * x + c6) * x + c5) * x + c4) * x + c3) * x + c2) * x + c1) * x + c0); + } + }; + + template + struct poly_impl + { + static inline T evaluate(const Type x, + const Type c6, const Type c5, const Type c4, const Type c3, const Type c2, + const Type c1, const Type c0) + { + // p(x) = c_6x^6 + c_5x^5 + c_4x^4 + c_3x^3 + c_2x^2 + c_1x^1 + c_0x^0 + return ((((((c6 * x + c5) * x + c4) * x + c3) * x + c2) * x + c1) * x + c0); + } + }; + + template + struct poly_impl + { + static inline T evaluate(const Type x, + const Type c5, const Type c4, const Type c3, const Type c2, + const Type c1, const Type c0) + { + // p(x) = c_5x^5 + c_4x^4 + c_3x^3 + c_2x^2 + c_1x^1 + c_0x^0 + return (((((c5 * x + c4) * x + c3) * x + c2) * x + c1) * x + c0); + } + }; + + template + struct poly_impl + { + static inline T evaluate(const Type x, const Type c4, const Type c3, const Type c2, const Type c1, const Type c0) + { + // p(x) = c_4x^4 + c_3x^3 + c_2x^2 + c_1x^1 + c_0x^0 + return ((((c4 * x + c3) * x + c2) * x + c1) * x + c0); + } + }; + + template + struct poly_impl + { + static inline T evaluate(const Type x, const Type c3, const Type c2, const Type c1, const Type c0) + { + // p(x) = c_3x^3 + c_2x^2 + c_1x^1 + c_0x^0 + return (((c3 * x + c2) * x + c1) * x + c0); + } + }; + + template + struct poly_impl + { + static inline T evaluate(const Type x, const Type c2, const Type c1, const Type c0) + { + // p(x) = c_2x^2 + c_1x^1 + c_0x^0 + return ((c2 * x + c1) * x + c0); + } + }; + + template + struct poly_impl + { + static inline T evaluate(const Type x, const Type c1, const Type c0) + { + // p(x) = c_1x^1 + c_0x^0 + return (c1 * x + c0); + } + }; + + public: + + using ifunction::operator(); + + polynomial() + : ifunction((N+2 <= 20) ? (N + 2) : std::numeric_limits::max()) + { + disable_has_side_effects(*this); + } + + virtual ~polynomial() exprtk_override + {} + + #define poly_rtrn(NN) \ + return (NN != N) ? std::numeric_limits::quiet_NaN() : + + inline virtual T operator() (const T& x, const T& c1, const T& c0) exprtk_override + { + poly_rtrn(1) (poly_impl::evaluate(x, c1, c0)); + } + + inline virtual T operator() (const T& x, const T& c2, const T& c1, const T& c0) exprtk_override + { + poly_rtrn(2) (poly_impl::evaluate(x, c2, c1, c0)); + } + + inline virtual T operator() (const T& x, const T& c3, const T& c2, const T& c1, const T& c0) exprtk_override + { + poly_rtrn(3) (poly_impl::evaluate(x, c3, c2, c1, c0)); + } + + inline virtual T operator() (const T& x, const T& c4, const T& c3, const T& c2, const T& c1, + const T& c0) exprtk_override + { + poly_rtrn(4) (poly_impl::evaluate(x, c4, c3, c2, c1, c0)); + } + + inline virtual T operator() (const T& x, const T& c5, const T& c4, const T& c3, const T& c2, + const T& c1, const T& c0) exprtk_override + { + poly_rtrn(5) (poly_impl::evaluate(x, c5, c4, c3, c2, c1, c0)); + } + + inline virtual T operator() (const T& x, const T& c6, const T& c5, const T& c4, const T& c3, + const T& c2, const T& c1, const T& c0) exprtk_override + { + poly_rtrn(6) (poly_impl::evaluate(x, c6, c5, c4, c3, c2, c1, c0)); + } + + inline virtual T operator() (const T& x, const T& c7, const T& c6, const T& c5, const T& c4, + const T& c3, const T& c2, const T& c1, const T& c0) exprtk_override + { + poly_rtrn(7) (poly_impl::evaluate(x, c7, c6, c5, c4, c3, c2, c1, c0)); + } + + inline virtual T operator() (const T& x, const T& c8, const T& c7, const T& c6, const T& c5, + const T& c4, const T& c3, const T& c2, const T& c1, const T& c0) exprtk_override + { + poly_rtrn(8) (poly_impl::evaluate(x, c8, c7, c6, c5, c4, c3, c2, c1, c0)); + } + + inline virtual T operator() (const T& x, const T& c9, const T& c8, const T& c7, const T& c6, + const T& c5, const T& c4, const T& c3, const T& c2, const T& c1, + const T& c0) exprtk_override + { + poly_rtrn(9) (poly_impl::evaluate(x, c9, c8, c7, c6, c5, c4, c3, c2, c1, c0)); + } + + inline virtual T operator() (const T& x, const T& c10, const T& c9, const T& c8, const T& c7, + const T& c6, const T& c5, const T& c4, const T& c3, const T& c2, + const T& c1, const T& c0) exprtk_override + { + poly_rtrn(10) (poly_impl::evaluate(x, c10, c9, c8, c7, c6, c5, c4, c3, c2, c1, c0)); + } + + inline virtual T operator() (const T& x, const T& c11, const T& c10, const T& c9, const T& c8, + const T& c7, const T& c6, const T& c5, const T& c4, const T& c3, + const T& c2, const T& c1, const T& c0) exprtk_override + { + poly_rtrn(11) (poly_impl::evaluate(x, c11, c10, c9, c8, c7, c6, c5, c4, c3, c2, c1, c0)); + } + + inline virtual T operator() (const T& x, const T& c12, const T& c11, const T& c10, const T& c9, + const T& c8, const T& c7, const T& c6, const T& c5, const T& c4, + const T& c3, const T& c2, const T& c1, const T& c0) exprtk_override + { + poly_rtrn(12) (poly_impl::evaluate(x, c12, c11, c10, c9, c8, c7, c6, c5, c4, c3, c2, c1, c0)); + } + + #undef poly_rtrn + + inline virtual T operator() () exprtk_override + { + return std::numeric_limits::quiet_NaN(); + } + + inline virtual T operator() (const T&) exprtk_override + { + return std::numeric_limits::quiet_NaN(); + } + + inline virtual T operator() (const T&, const T&) exprtk_override + { + return std::numeric_limits::quiet_NaN(); + } + }; + + template + class function_compositor + { + public: + + typedef exprtk::expression expression_t; + typedef exprtk::symbol_table symbol_table_t; + typedef exprtk::parser parser_t; + typedef typename parser_t::settings_store settings_t; + + struct function + { + function() + {} + + explicit function(const std::string& n) + : name_(n) + {} + + function(const std::string& name, + const std::string& expression) + : name_(name) + , expression_(expression) + {} + + function(const std::string& name, + const std::string& expression, + const std::string& v0) + : name_(name) + , expression_(expression) + { + v_.push_back(v0); + } + + function(const std::string& name, + const std::string& expression, + const std::string& v0, const std::string& v1) + : name_(name) + , expression_(expression) + { + v_.push_back(v0); v_.push_back(v1); + } + + function(const std::string& name, + const std::string& expression, + const std::string& v0, const std::string& v1, + const std::string& v2) + : name_(name) + , expression_(expression) + { + v_.push_back(v0); v_.push_back(v1); + v_.push_back(v2); + } + + function(const std::string& name, + const std::string& expression, + const std::string& v0, const std::string& v1, + const std::string& v2, const std::string& v3) + : name_(name) + , expression_(expression) + { + v_.push_back(v0); v_.push_back(v1); + v_.push_back(v2); v_.push_back(v3); + } + + function(const std::string& name, + const std::string& expression, + const std::string& v0, const std::string& v1, + const std::string& v2, const std::string& v3, + const std::string& v4) + : name_(name) + , expression_(expression) + { + v_.push_back(v0); v_.push_back(v1); + v_.push_back(v2); v_.push_back(v3); + v_.push_back(v4); + } + + inline function& name(const std::string& n) + { + name_ = n; + return (*this); + } + + inline function& expression(const std::string& e) + { + expression_ = e; + return (*this); + } + + inline function& var(const std::string& v) + { + v_.push_back(v); + return (*this); + } + + inline function& vars(const std::string& v0, + const std::string& v1) + { + v_.push_back(v0); + v_.push_back(v1); + return (*this); + } + + inline function& vars(const std::string& v0, + const std::string& v1, + const std::string& v2) + { + v_.push_back(v0); + v_.push_back(v1); + v_.push_back(v2); + return (*this); + } + + inline function& vars(const std::string& v0, + const std::string& v1, + const std::string& v2, + const std::string& v3) + { + v_.push_back(v0); + v_.push_back(v1); + v_.push_back(v2); + v_.push_back(v3); + return (*this); + } + + inline function& vars(const std::string& v0, + const std::string& v1, + const std::string& v2, + const std::string& v3, + const std::string& v4) + { + v_.push_back(v0); + v_.push_back(v1); + v_.push_back(v2); + v_.push_back(v3); + v_.push_back(v4); + return (*this); + } + + std::string name_; + std::string expression_; + std::deque v_; + }; + + private: + + struct base_func : public exprtk::ifunction + { + typedef const T& type; + typedef exprtk::ifunction function_t; + typedef std::vector varref_t; + typedef std::vector var_t; + typedef std::vector str_t; + typedef std::pair lvarref_t; + typedef std::vector lvr_vec_t; + typedef std::vector lstr_vec_t; + + using exprtk::ifunction::operator(); + + explicit base_func(const std::size_t& pc = 0) + : exprtk::ifunction(pc) + , local_var_stack_size(0) + , stack_depth(0) + { + v.resize(pc); + } + + virtual ~base_func() + {} + + #define exprtk_assign(Index) \ + (*v[Index]) = v##Index; \ + + inline void update(const T& v0) + { + exprtk_assign(0) + } + + inline void update(const T& v0, const T& v1) + { + exprtk_assign(0) exprtk_assign(1) + } + + inline void update(const T& v0, const T& v1, const T& v2) + { + exprtk_assign(0) exprtk_assign(1) + exprtk_assign(2) + } + + inline void update(const T& v0, const T& v1, const T& v2, const T& v3) + { + exprtk_assign(0) exprtk_assign(1) + exprtk_assign(2) exprtk_assign(3) + } + + inline void update(const T& v0, const T& v1, const T& v2, const T& v3, const T& v4) + { + exprtk_assign(0) exprtk_assign(1) + exprtk_assign(2) exprtk_assign(3) + exprtk_assign(4) + } + + inline void update(const T& v0, const T& v1, const T& v2, const T& v3, const T& v4, const T& v5) + { + exprtk_assign(0) exprtk_assign(1) + exprtk_assign(2) exprtk_assign(3) + exprtk_assign(4) exprtk_assign(5) + } + + #ifdef exprtk_assign + #undef exprtk_assign + #endif + + inline function_t& setup(expression_t& expr) + { + expression = expr; + + typedef typename expression_t::control_block ctrlblk_t; + typedef typename ctrlblk_t::local_data_list_t ldl_t; + typedef typename ctrlblk_t::data_type data_t; + typedef typename ldl_t::value_type ldl_value_type; + + const ldl_t ldl = expr.local_data_list(); + + std::vector > index_list; + + for (std::size_t i = 0; i < ldl.size(); ++i) + { + exprtk_debug(("base_func::setup() - element[%02d] type: %s size: %d\n", + static_cast(i), + expression_t::control_block::to_str(ldl[i].type).c_str(), + static_cast(ldl[i].size))); + + switch (ldl[i].type) + { + case ctrlblk_t::e_unknown : continue; + case ctrlblk_t::e_expr : continue; + case ctrlblk_t::e_vecholder : continue; + default : break; + } + + if (ldl[i].size) + { + index_list.push_back(std::make_pair(i,ldl[i].type)); + } + } + + std::size_t input_param_count = 0; + + for (std::size_t i = 0; i < index_list.size(); ++i) + { + const std::size_t index = index_list[i].first; + const ldl_value_type& local_var = ldl[index]; + + assert(local_var.pointer); + + if (i < (index_list.size() - v.size())) + { + if (local_var.type == ctrlblk_t::e_string) + { + local_str_vars.push_back( + reinterpret_cast(local_var.pointer)); + } + else if ( + (local_var.type == ctrlblk_t::e_data ) || + (local_var.type == ctrlblk_t::e_vecdata) + ) + { + local_vars.push_back(std::make_pair( + reinterpret_cast(local_var.pointer), + local_var.size)); + + local_var_stack_size += local_var.size; + } + } + else + { + v[input_param_count++] = reinterpret_cast(local_var.pointer); + } + } + + clear_stack(); + + return (*this); + } + + inline void pre() + { + if (stack_depth++) + { + if (!v.empty()) + { + var_t var_stack(v.size(),T(0)); + copy(v,var_stack); + input_params_stack.push_back(var_stack); + } + + if (!local_vars.empty()) + { + var_t local_vec_frame(local_var_stack_size,T(0)); + copy(local_vars,local_vec_frame); + local_var_stack.push_back(local_vec_frame); + } + + if (!local_str_vars.empty()) + { + str_t local_str_frame(local_str_vars.size()); + copy(local_str_vars,local_str_frame); + local_str_stack.push_back(local_str_frame); + } + } + } + + inline void post() + { + if (--stack_depth) + { + if (!v.empty()) + { + copy(input_params_stack.back(), v); + input_params_stack.pop_back(); + } + + if (!local_vars.empty()) + { + copy(local_var_stack.back(), local_vars); + local_var_stack.pop_back(); + } + + if (!local_str_vars.empty()) + { + copy(local_str_stack.back(), local_str_vars); + local_str_stack.pop_back(); + } + } + } + + void copy(const varref_t& src_v, var_t& dest_v) + { + for (std::size_t i = 0; i < src_v.size(); ++i) + { + dest_v[i] = (*src_v[i]); + } + } + + void copy(const lstr_vec_t& src_v, str_t& dest_v) + { + for (std::size_t i = 0; i < src_v.size(); ++i) + { + dest_v[i] = (*src_v[i]); + } + } + + void copy(const var_t& src_v, varref_t& dest_v) + { + for (std::size_t i = 0; i < src_v.size(); ++i) + { + (*dest_v[i]) = src_v[i]; + } + } + + void copy(const lvr_vec_t& src_v, var_t& dest_v) + { + typename var_t::iterator itr = dest_v.begin(); + typedef typename std::iterator_traits::difference_type diff_t; + + for (std::size_t i = 0; i < src_v.size(); ++i) + { + lvarref_t vr = src_v[i]; + + if (1 == vr.second) + *itr++ = (*vr.first); + else + { + std::copy(vr.first, vr.first + vr.second, itr); + itr += static_cast(vr.second); + } + } + } + + void copy(const var_t& src_v, lvr_vec_t& dest_v) + { + typename var_t::const_iterator itr = src_v.begin(); + typedef typename std::iterator_traits::difference_type diff_t; + + for (std::size_t i = 0; i < dest_v.size(); ++i) + { + lvarref_t& vr = dest_v[i]; + + assert(vr.first != 0); + assert(vr.second > 0); + + if (1 == vr.second) + (*vr.first) = *itr++; + else + { + std::copy(itr, itr + static_cast(vr.second), vr.first); + itr += static_cast(vr.second); + } + } + } + + void copy(const str_t& src_str, lstr_vec_t& dest_str) + { + assert(src_str.size() == dest_str.size()); + + for (std::size_t i = 0; i < dest_str.size(); ++i) + { + *dest_str[i] = src_str[i]; + } + } + + inline void clear_stack() + { + for (std::size_t i = 0; i < v.size(); ++i) + { + (*v[i]) = 0; + } + } + + inline virtual T value(expression_t& e) + { + return e.value(); + } + + expression_t expression; + varref_t v; + lvr_vec_t local_vars; + lstr_vec_t local_str_vars; + std::size_t local_var_stack_size; + std::size_t stack_depth; + std::deque input_params_stack; + std::deque local_var_stack; + std::deque local_str_stack; + }; + + typedef std::map funcparam_t; + + typedef const T& type; + + template + struct scoped_bft + { + explicit scoped_bft(BaseFuncType& bft) + : bft_(bft) + { + bft_.pre (); + } + + ~scoped_bft() + { + bft_.post(); + } + + BaseFuncType& bft_; + + private: + + scoped_bft(const scoped_bft&) exprtk_delete; + scoped_bft& operator=(const scoped_bft&) exprtk_delete; + }; + + struct func_0param : public base_func + { + using exprtk::ifunction::operator(); + + func_0param() : base_func(0) {} + + inline T operator() () exprtk_override + { + scoped_bft sb(*this); + return this->value(base_func::expression); + } + }; + + struct func_1param : public base_func + { + using exprtk::ifunction::operator(); + + func_1param() : base_func(1) {} + + inline T operator() (type v0) exprtk_override + { + scoped_bft sb(*this); + base_func::update(v0); + return this->value(base_func::expression); + } + }; + + struct func_2param : public base_func + { + using exprtk::ifunction::operator(); + + func_2param() : base_func(2) {} + + inline T operator() (type v0, type v1) exprtk_override + { + scoped_bft sb(*this); + base_func::update(v0, v1); + return this->value(base_func::expression); + } + }; + + struct func_3param : public base_func + { + using exprtk::ifunction::operator(); + + func_3param() : base_func(3) {} + + inline T operator() (type v0, type v1, type v2) exprtk_override + { + scoped_bft sb(*this); + base_func::update(v0, v1, v2); + return this->value(base_func::expression); + } + }; + + struct func_4param : public base_func + { + using exprtk::ifunction::operator(); + + func_4param() : base_func(4) {} + + inline T operator() (type v0, type v1, type v2, type v3) exprtk_override + { + scoped_bft sb(*this); + base_func::update(v0, v1, v2, v3); + return this->value(base_func::expression); + } + }; + + struct func_5param : public base_func + { + using exprtk::ifunction::operator(); + + func_5param() : base_func(5) {} + + inline T operator() (type v0, type v1, type v2, type v3, type v4) exprtk_override + { + scoped_bft sb(*this); + base_func::update(v0, v1, v2, v3, v4); + return this->value(base_func::expression); + } + }; + + struct func_6param : public base_func + { + using exprtk::ifunction::operator(); + + func_6param() : base_func(6) {} + + inline T operator() (type v0, type v1, type v2, type v3, type v4, type v5) exprtk_override + { + scoped_bft sb(*this); + base_func::update(v0, v1, v2, v3, v4, v5); + return this->value(base_func::expression); + } + }; + + static T return_value(expression_t& e) + { + typedef exprtk::results_context results_context_t; + typedef typename results_context_t::type_store_t type_t; + typedef typename type_t::scalar_view scalar_t; + + const T result = e.value(); + + if (e.return_invoked()) + { + // Due to the post compilation checks, it can be safely + // assumed that there will be at least one parameter + // and that the first parameter will always be scalar. + return scalar_t(e.results()[0])(); + } + + return result; + } + + #define def_fp_retval(N) \ + struct func_##N##param_retval exprtk_final : public func_##N##param \ + { \ + inline T value(expression_t& e) exprtk_override \ + { \ + return return_value(e); \ + } \ + }; \ + + def_fp_retval(0) + def_fp_retval(1) + def_fp_retval(2) + def_fp_retval(3) + def_fp_retval(4) + def_fp_retval(5) + def_fp_retval(6) + + #undef def_fp_retval + + template class Sequence> + inline bool add(const std::string& name, + const std::string& expression, + const Sequence& var_list, + const bool override = false) + { + const typename std::map::iterator itr = expr_map_.find(name); + + if (expr_map_.end() != itr) + { + if (!override) + { + exprtk_debug(("Compositor error(add): function '%s' already defined\n", + name.c_str())); + + return false; + } + + remove(name, var_list.size()); + } + + if (compile_expression(name, expression, var_list)) + { + const std::size_t n = var_list.size(); + + fp_map_[n][name]->setup(expr_map_[name]); + + return true; + } + else + { + exprtk_debug(("Compositor error(add): Failed to compile function '%s'\n", + name.c_str())); + + return false; + } + } + + public: + + function_compositor() + : parser_(settings_t::default_compile_all_opts + + settings_t::e_disable_zero_return) + , fp_map_(7) + , load_variables_(false) + , load_vectors_(false) + {} + + explicit function_compositor(const symbol_table_t& st) + : symbol_table_(st) + , parser_(settings_t::default_compile_all_opts + + settings_t::e_disable_zero_return) + , fp_map_(7) + , load_variables_(false) + , load_vectors_(false) + {} + + ~function_compositor() + { + clear(); + } + + inline symbol_table_t& symbol_table() + { + return symbol_table_; + } + + inline const symbol_table_t& symbol_table() const + { + return symbol_table_; + } + + inline void add_auxiliary_symtab(symbol_table_t& symtab) + { + auxiliary_symtab_list_.push_back(&symtab); + } + + void load_variables(const bool load = true) + { + load_variables_ = load; + } + + void load_vectors(const bool load = true) + { + load_vectors_ = load; + } + + inline void register_loop_runtime_check(loop_runtime_check& lrtchk) + { + parser_.register_loop_runtime_check(lrtchk); + } + + inline void register_vector_access_runtime_check(vector_access_runtime_check& vartchk) + { + parser_.register_vector_access_runtime_check(vartchk); + } + + inline void register_compilation_timeout_check(compilation_check& compchk) + { + parser_.register_compilation_timeout_check(compchk); + } + + inline void clear_loop_runtime_check() + { + parser_.clear_loop_runtime_check(); + } + + inline void clear_vector_access_runtime_check() + { + parser_.clear_vector_access_runtime_check(); + } + + inline void clear_compilation_timeout_check() + { + parser_.clear_compilation_timeout_check(); + } + + void clear() + { + symbol_table_.clear(); + expr_map_ .clear(); + + for (std::size_t i = 0; i < fp_map_.size(); ++i) + { + typename funcparam_t::iterator itr = fp_map_[i].begin(); + typename funcparam_t::iterator end = fp_map_[i].end (); + + while (itr != end) + { + delete itr->second; + ++itr; + } + + fp_map_[i].clear(); + } + + clear_loop_runtime_check (); + clear_vector_access_runtime_check(); + clear_compilation_timeout_check (); + } + + inline bool add(const function& f, const bool override = false) + { + return add(f.name_, f.expression_, f.v_,override); + } + + inline std::string error() const + { + if (!error_list_.empty()) + { + return error_list_[0].diagnostic; + } + else + return std::string("No Error"); + } + + inline std::size_t error_count() const + { + return error_list_.size(); + } + + inline parser_error::type get_error(const std::size_t& index) const + { + if (index < error_list_.size()) + { + return error_list_[index]; + } + + throw std::invalid_argument("compositor::get_error() - Invalid error index specified"); + } + + private: + + template class Sequence> + bool compile_expression(const std::string& name, + const std::string& expression, + const Sequence& input_var_list, + bool return_present = false) + { + expression_t compiled_expression; + symbol_table_t local_symbol_table; + + local_symbol_table.load_from(symbol_table_); + local_symbol_table.add_constants(); + + if (load_variables_) + { + local_symbol_table.load_variables_from(symbol_table_); + } + + if (load_vectors_) + { + local_symbol_table.load_vectors_from(symbol_table_); + } + + error_list_.clear(); + + if (!valid(name,input_var_list.size())) + { + parser_error::type error = + parser_error::make_error( + parser_error::e_parser, + lexer::token(), + "ERR283 - Function '" + name + "' is an invalid overload", + exprtk_error_location); + + error_list_.push_back(error); + return false; + } + + if (!forward(name, + input_var_list.size(), + local_symbol_table, + return_present)) + return false; + + compiled_expression.register_symbol_table(local_symbol_table); + + for (std::size_t i = 0; i < auxiliary_symtab_list_.size(); ++i) + { + compiled_expression.register_symbol_table((*auxiliary_symtab_list_[i])); + } + + std::string mod_expression; + + for (std::size_t i = 0; i < input_var_list.size(); ++i) + { + mod_expression += " var " + input_var_list[i] + "{};\n"; + } + + if ( + ('{' == details::front(expression)) && + ('}' == details::back (expression)) + ) + mod_expression += "~" + expression + ";"; + else + mod_expression += "~{" + expression + "};"; + + if (!parser_.compile(mod_expression,compiled_expression)) + { + exprtk_debug(("Compositor Error: %s\n", parser_.error().c_str())); + exprtk_debug(("Compositor modified expression: \n%s\n", mod_expression.c_str())); + + remove(name,input_var_list.size()); + + for (std::size_t err_index = 0; err_index < parser_.error_count(); ++err_index) + { + error_list_.push_back(parser_.get_error(err_index)); + } + + return false; + } + + if (!return_present && parser_.dec().return_present()) + { + remove(name,input_var_list.size()); + return compile_expression(name, expression, input_var_list, true); + } + + // Make sure every return point has a scalar as its first parameter + if (parser_.dec().return_present()) + { + typedef std::vector str_list_t; + + str_list_t ret_param_list = parser_.dec().return_param_type_list(); + + for (std::size_t i = 0; i < ret_param_list.size(); ++i) + { + const std::string& params = ret_param_list[i]; + + if (params.empty() || ('T' != params[0])) + { + exprtk_debug(("Compositor Error: Return statement in function '%s' is invalid\n", + name.c_str())); + + remove(name,input_var_list.size()); + + return false; + } + } + } + + expr_map_[name] = compiled_expression; + + exprtk::ifunction& ifunc = (*(fp_map_[input_var_list.size()])[name]); + + if (symbol_table_.add_function(name,ifunc)) + return true; + else + { + exprtk_debug(("Compositor Error: Failed to add function '%s' to symbol table\n", + name.c_str())); + return false; + } + } + + inline bool symbol_used(const std::string& symbol) const + { + return ( + symbol_table_.is_variable (symbol) || + symbol_table_.is_stringvar (symbol) || + symbol_table_.is_function (symbol) || + symbol_table_.is_vector (symbol) || + symbol_table_.is_vararg_function(symbol) + ); + } + + inline bool valid(const std::string& name, + const std::size_t& arg_count) const + { + if (arg_count > 6) + return false; + else if (symbol_used(name)) + return false; + else if (fp_map_[arg_count].end() != fp_map_[arg_count].find(name)) + return false; + else + return true; + } + + inline bool forward(const std::string& name, + const std::size_t& arg_count, + symbol_table_t& sym_table, + const bool ret_present = false) + { + switch (arg_count) + { + #define case_stmt(N) \ + case N : (fp_map_[arg_count])[name] = \ + (!ret_present) ? static_cast \ + (new func_##N##param) : \ + static_cast \ + (new func_##N##param_retval) ; \ + break; \ + + case_stmt(0) case_stmt(1) case_stmt(2) + case_stmt(3) case_stmt(4) case_stmt(5) + case_stmt(6) + #undef case_stmt + } + + exprtk::ifunction& ifunc = (*(fp_map_[arg_count])[name]); + + return sym_table.add_function(name,ifunc); + } + + inline void remove(const std::string& name, const std::size_t& arg_count) + { + if (arg_count > 6) + return; + + const typename std::map::iterator em_itr = expr_map_.find(name); + + if (expr_map_.end() != em_itr) + { + expr_map_.erase(em_itr); + } + + const typename funcparam_t::iterator fp_itr = fp_map_[arg_count].find(name); + + if (fp_map_[arg_count].end() != fp_itr) + { + delete fp_itr->second; + fp_map_[arg_count].erase(fp_itr); + } + + symbol_table_.remove_function(name); + } + + private: + + symbol_table_t symbol_table_; + parser_t parser_; + std::map expr_map_; + std::vector fp_map_; + std::vector auxiliary_symtab_list_; + std::deque error_list_; + bool load_variables_; + bool load_vectors_; + }; // class function_compositor + +} // namespace exprtk + +#if defined(_MSC_VER) || defined(_WIN32) || defined(__WIN32__) || defined(WIN32) +# ifndef NOMINMAX +# define NOMINMAX +# endif +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include +# include +#else +# include +# include +# include +#endif + +namespace exprtk +{ + class timer + { + public: + + #if defined(_MSC_VER) || defined(_WIN32) || defined(__WIN32__) || defined(WIN32) + timer() + : in_use_(false) + , start_time_{ {0, 0} } + , stop_time_ { {0, 0} } + { + QueryPerformanceFrequency(&clock_frequency_); + } + + inline void start() + { + in_use_ = true; + QueryPerformanceCounter(&start_time_); + } + + inline void stop() + { + QueryPerformanceCounter(&stop_time_); + in_use_ = false; + } + + inline double time() const + { + return (1.0 * (stop_time_.QuadPart - start_time_.QuadPart)) / (1.0 * clock_frequency_.QuadPart); + } + + #else + + timer() + : in_use_(false) + { + start_time_.tv_sec = 0; + start_time_.tv_usec = 0; + + stop_time_.tv_sec = 0; + stop_time_.tv_usec = 0; + } + + inline void start() + { + in_use_ = true; + gettimeofday(&start_time_,0); + } + + inline void stop() + { + gettimeofday(&stop_time_, 0); + in_use_ = false; + } + + inline unsigned long long int usec_time() const + { + if (!in_use_) + { + if (stop_time_.tv_sec >= start_time_.tv_sec) + { + return 1000000LLU * static_cast(stop_time_.tv_sec - start_time_.tv_sec ) + + static_cast(stop_time_.tv_usec - start_time_.tv_usec) ; + } + else + return std::numeric_limits::max(); + } + else + return std::numeric_limits::max(); + } + + inline double time() const + { + return usec_time() * 0.000001; + } + + #endif + + inline bool in_use() const + { + return in_use_; + } + + private: + + bool in_use_; + + #if defined(_MSC_VER) || defined(_WIN32) || defined(__WIN32__) || defined(WIN32) + LARGE_INTEGER start_time_; + LARGE_INTEGER stop_time_; + LARGE_INTEGER clock_frequency_; + #else + struct timeval start_time_; + struct timeval stop_time_; + #endif + }; + + template + struct type_defs + { + typedef symbol_table symbol_table_t; + typedef expression expression_t; + typedef parser parser_t; + typedef parser_error::type error_t; + typedef function_compositor compositor_t; + typedef typename compositor_t::function function_t; + }; + +} // namespace exprtk + +#ifndef exprtk_disable_rtl_io +namespace exprtk +{ + namespace rtl { namespace io { namespace details + { + template + inline void print_type(const std::string& fmt, + const T v, + exprtk::details::numeric::details::real_type_tag) + { + #if defined(__clang__) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wformat-nonliteral" + #elif defined(__GNUC__) || defined(__GNUG__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wformat-nonliteral" + #elif defined(_MSC_VER) + #endif + + printf(fmt.c_str(), v); + + #if defined(__clang__) + #pragma clang diagnostic pop + #elif defined(__GNUC__) || defined(__GNUG__) + #pragma GCC diagnostic pop + #elif defined(_MSC_VER) + #endif + } + + template + struct print_impl + { + typedef typename igeneric_function::generic_type generic_type; + typedef typename igeneric_function::parameter_list_t parameter_list_t; + typedef typename generic_type::scalar_view scalar_t; + typedef typename generic_type::vector_view vector_t; + typedef typename generic_type::string_view string_t; + typedef typename exprtk::details::numeric::details::number_type::type num_type; + + static void process(const std::string& scalar_format, parameter_list_t parameters) + { + for (std::size_t i = 0; i < parameters.size(); ++i) + { + generic_type& gt = parameters[i]; + + switch (gt.type) + { + case generic_type::e_scalar : print(scalar_format,scalar_t(gt)); + break; + + case generic_type::e_vector : print(scalar_format,vector_t(gt)); + break; + + case generic_type::e_string : print(string_t(gt)); + break; + + default : continue; + } + } + } + + static inline void print(const std::string& scalar_format, const scalar_t& s) + { + print_type(scalar_format,s(),num_type()); + } + + static inline void print(const std::string& scalar_format, const vector_t& v) + { + for (std::size_t i = 0; i < v.size(); ++i) + { + print_type(scalar_format,v[i],num_type()); + + if ((i + 1) < v.size()) + printf(" "); + } + } + + static inline void print(const string_t& s) + { + printf("%s",to_str(s).c_str()); + } + }; + + } // namespace exprtk::rtl::io::details + + template + struct print exprtk_final : public exprtk::igeneric_function + { + typedef typename igeneric_function::parameter_list_t parameter_list_t; + + using exprtk::igeneric_function::operator(); + + explicit print(const std::string& scalar_format = "%10.5f") + : scalar_format_(scalar_format) + { + exprtk::enable_zero_parameters(*this); + } + + inline T operator() (parameter_list_t parameters) exprtk_override + { + details::print_impl::process(scalar_format_,parameters); + return T(0); + } + + std::string scalar_format_; + }; + + template + struct println exprtk_final : public exprtk::igeneric_function + { + typedef typename igeneric_function::parameter_list_t parameter_list_t; + + using exprtk::igeneric_function::operator(); + + explicit println(const std::string& scalar_format = "%10.5f") + : scalar_format_(scalar_format) + { + exprtk::enable_zero_parameters(*this); + } + + inline T operator() (parameter_list_t parameters) exprtk_override + { + details::print_impl::process(scalar_format_,parameters); + printf("\n"); + return T(0); + } + + std::string scalar_format_; + }; + + template + struct package + { + print p; + println pl; + + bool register_package(exprtk::symbol_table& symtab) + { + #define exprtk_register_function(FunctionName, FunctionType) \ + if (!symtab.add_function(FunctionName,FunctionType)) \ + { \ + exprtk_debug(( \ + "exprtk::rtl::io::register_package - Failed to add function: %s\n", \ + FunctionName)); \ + return false; \ + } \ + + exprtk_register_function("print" , p ) + exprtk_register_function("println", pl) + #undef exprtk_register_function + + return true; + } + }; + + } // namespace exprtk::rtl::io + } // namespace exprtk::rtl +} // namespace exprtk +#endif + +#ifndef exprtk_disable_rtl_io_file +#include +namespace exprtk +{ + namespace rtl { namespace io { namespace file { namespace details + { + using ::exprtk::details::char_ptr; + using ::exprtk::details::char_cptr; + + enum file_mode + { + e_error = 0, + e_read = 1, + e_write = 2, + e_rdwrt = 4 + }; + + struct file_descriptor + { + file_descriptor(const std::string& fname, const std::string& access) + : stream_ptr(0) + , mode(get_file_mode(access)) + , file_name(fname) + {} + + void* stream_ptr; + file_mode mode; + std::string file_name; + + bool open() + { + if (e_read == mode) + { + std::ifstream* stream = new std::ifstream(file_name.c_str(),std::ios::binary); + + if (!(*stream)) + { + file_name.clear(); + delete stream; + + return false; + } + + stream_ptr = stream; + + return true; + } + else if (e_write == mode) + { + std::ofstream* stream = new std::ofstream(file_name.c_str(),std::ios::binary); + + if (!(*stream)) + { + file_name.clear(); + delete stream; + + return false; + } + + stream_ptr = stream; + + return true; + } + else if (e_rdwrt == mode) + { + std::fstream* stream = new std::fstream(file_name.c_str(),std::ios::binary); + + if (!(*stream)) + { + file_name.clear(); + delete stream; + + return false; + } + + stream_ptr = stream; + + return true; + } + + return false; + } + + template + void close(Ptr& p) + { + Stream* stream = reinterpret_cast(p); + stream->close(); + delete stream; + p = reinterpret_cast(0); + } + + bool close() + { + switch (mode) + { + case e_read : close(stream_ptr); + break; + + case e_write : close(stream_ptr); + break; + + case e_rdwrt : close (stream_ptr); + break; + + default : return false; + } + + return true; + } + + template + bool write(const View& view, const std::size_t amount, const std::size_t offset = 0) + { + switch (mode) + { + case e_write : reinterpret_cast(stream_ptr)-> + write(reinterpret_cast(view.begin() + offset), amount * sizeof(typename View::value_t)); + break; + + case e_rdwrt : reinterpret_cast(stream_ptr)-> + write(reinterpret_cast(view.begin() + offset) , amount * sizeof(typename View::value_t)); + break; + + default : return false; + } + + return true; + } + + template + bool read(View& view, const std::size_t amount, const std::size_t offset = 0) + { + switch (mode) + { + case e_read : reinterpret_cast(stream_ptr)-> + read(reinterpret_cast(view.begin() + offset), amount * sizeof(typename View::value_t)); + break; + + case e_rdwrt : reinterpret_cast(stream_ptr)-> + read(reinterpret_cast(view.begin() + offset) , amount * sizeof(typename View::value_t)); + break; + + default : return false; + } + + return true; + } + + bool getline(std::string& s) + { + switch (mode) + { + case e_read : return (!!std::getline(*reinterpret_cast(stream_ptr),s)); + case e_rdwrt : return (!!std::getline(*reinterpret_cast(stream_ptr),s)); + default : return false; + } + } + + bool eof() const + { + switch (mode) + { + case e_read : return reinterpret_cast(stream_ptr)->eof(); + case e_write : return reinterpret_cast(stream_ptr)->eof(); + case e_rdwrt : return reinterpret_cast(stream_ptr)->eof(); + default : return true; + } + } + + file_mode get_file_mode(const std::string& access) const + { + if (access.empty() || access.size() > 2) + return e_error; + + std::size_t w_cnt = 0; + std::size_t r_cnt = 0; + + for (std::size_t i = 0; i < access.size(); ++i) + { + switch (std::tolower(access[i])) + { + case 'r' : r_cnt++; break; + case 'w' : w_cnt++; break; + default : return e_error; + } + } + + if ((0 == r_cnt) && (0 == w_cnt)) + return e_error; + else if ((r_cnt > 1) || (w_cnt > 1)) + return e_error; + else if ((1 == r_cnt) && (1 == w_cnt)) + return e_rdwrt; + else if (1 == r_cnt) + return e_read; + else + return e_write; + } + }; + + template + file_descriptor* make_handle(T v) + { + const std::size_t fd_size = sizeof(details::file_descriptor*); + details::file_descriptor* fd = reinterpret_cast(0); + + std::memcpy(reinterpret_cast(&fd), + reinterpret_cast(&v ), + fd_size); + return fd; + } + + template + void perform_check() + { + #ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable: 4127) + #endif + if (sizeof(T) < sizeof(void*)) + { + throw std::runtime_error("exprtk::rtl::io::file - Error - pointer size larger than holder."); + } + #ifdef _MSC_VER + #pragma warning(pop) + #endif + assert(sizeof(T) <= sizeof(void*)); + } + + } // namespace exprtk::rtl::io::file::details + + template + class open exprtk_final : public exprtk::igeneric_function + { + public: + + typedef typename exprtk::igeneric_function igfun_t; + typedef typename igfun_t::parameter_list_t parameter_list_t; + typedef typename igfun_t::generic_type generic_type; + typedef typename generic_type::string_view string_t; + + using igfun_t::operator(); + + open() + : exprtk::igeneric_function("S|SS") + { details::perform_check(); } + + inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override + { + const std::string file_name = to_str(string_t(parameters[0])); + + if (file_name.empty()) + { + return T(0); + } + + if ((1 == ps_index) && (0 == string_t(parameters[1]).size())) + { + return T(0); + } + + const std::string access = + (0 == ps_index) ? "r" : to_str(string_t(parameters[1])); + + details::file_descriptor* fd = new details::file_descriptor(file_name,access); + + if (fd->open()) + { + T t = T(0); + + const std::size_t fd_size = sizeof(details::file_descriptor*); + + std::memcpy(reinterpret_cast(&t ), + reinterpret_cast(&fd), + fd_size); + return t; + } + else + { + delete fd; + return T(0); + } + } + }; + + template + struct close exprtk_final : public exprtk::ifunction + { + using exprtk::ifunction::operator(); + + close() + : exprtk::ifunction(1) + { details::perform_check(); } + + inline T operator() (const T& v) exprtk_override + { + details::file_descriptor* fd = details::make_handle(v); + + if (!fd->close()) + return T(0); + + delete fd; + + return T(1); + } + }; + + template + class write exprtk_final : public exprtk::igeneric_function + { + public: + + typedef typename exprtk::igeneric_function igfun_t; + typedef typename igfun_t::parameter_list_t parameter_list_t; + typedef typename igfun_t::generic_type generic_type; + typedef typename generic_type::string_view string_t; + typedef typename generic_type::scalar_view scalar_t; + typedef typename generic_type::vector_view vector_t; + + using igfun_t::operator(); + + write() + : igfun_t("TS|TST|TV|TVT") + { details::perform_check(); } + + inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override + { + details::file_descriptor* fd = details::make_handle(scalar_t(parameters[0])()); + + switch (ps_index) + { + case 0 : { + const string_t buffer(parameters[1]); + const std::size_t amount = buffer.size(); + return T(fd->write(buffer, amount) ? 1 : 0); + } + + case 1 : { + const string_t buffer(parameters[1]); + const std::size_t amount = + std::min(buffer.size(), + static_cast(scalar_t(parameters[2])())); + return T(fd->write(buffer, amount) ? 1 : 0); + } + + case 2 : { + const vector_t vec(parameters[1]); + const std::size_t amount = vec.size(); + return T(fd->write(vec, amount) ? 1 : 0); + } + + case 3 : { + const vector_t vec(parameters[1]); + const std::size_t amount = + std::min(vec.size(), + static_cast(scalar_t(parameters[2])())); + return T(fd->write(vec, amount) ? 1 : 0); + } + } + + return T(0); + } + }; + + template + class read exprtk_final : public exprtk::igeneric_function + { + public: + + typedef typename exprtk::igeneric_function igfun_t; + typedef typename igfun_t::parameter_list_t parameter_list_t; + typedef typename igfun_t::generic_type generic_type; + typedef typename generic_type::string_view string_t; + typedef typename generic_type::scalar_view scalar_t; + typedef typename generic_type::vector_view vector_t; + + using igfun_t::operator(); + + read() + : igfun_t("TS|TST|TV|TVT") + { details::perform_check(); } + + inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override + { + details::file_descriptor* fd = details::make_handle(scalar_t(parameters[0])()); + + switch (ps_index) + { + case 0 : { + string_t buffer(parameters[1]); + const std::size_t amount = buffer.size(); + return T(fd->read(buffer,amount) ? 1 : 0); + } + + case 1 : { + string_t buffer(parameters[1]); + const std::size_t amount = + std::min(buffer.size(), + static_cast(scalar_t(parameters[2])())); + return T(fd->read(buffer,amount) ? 1 : 0); + } + + case 2 : { + vector_t vec(parameters[1]); + const std::size_t amount = vec.size(); + return T(fd->read(vec,amount) ? 1 : 0); + } + + case 3 : { + vector_t vec(parameters[1]); + const std::size_t amount = + std::min(vec.size(), + static_cast(scalar_t(parameters[2])())); + return T(fd->read(vec,amount) ? 1 : 0); + } + } + + return T(0); + } + }; + + template + class getline exprtk_final : public exprtk::igeneric_function + { + public: + + typedef typename exprtk::igeneric_function igfun_t; + typedef typename igfun_t::parameter_list_t parameter_list_t; + typedef typename igfun_t::generic_type generic_type; + typedef typename generic_type::string_view string_t; + typedef typename generic_type::scalar_view scalar_t; + + using igfun_t::operator(); + + getline() + : igfun_t("T",igfun_t::e_rtrn_string) + { details::perform_check(); } + + inline T operator() (std::string& result, parameter_list_t parameters) exprtk_override + { + details::file_descriptor* fd = details::make_handle(scalar_t(parameters[0])()); + return T(fd->getline(result) ? 1 : 0); + } + }; + + template + struct eof exprtk_final : public exprtk::ifunction + { + using exprtk::ifunction::operator(); + + eof() + : exprtk::ifunction(1) + { details::perform_check(); } + + inline T operator() (const T& v) exprtk_override + { + details::file_descriptor* fd = details::make_handle(v); + return (fd->eof() ? T(1) : T(0)); + } + }; + + template + struct package + { + open o; + close c; + write w; + read r; + getline g; + eof e; + + bool register_package(exprtk::symbol_table& symtab) + { + #define exprtk_register_function(FunctionName, FunctionType) \ + if (!symtab.add_function(FunctionName,FunctionType)) \ + { \ + exprtk_debug(( \ + "exprtk::rtl::io::file::register_package - Failed to add function: %s\n", \ + FunctionName)); \ + return false; \ + } \ + + exprtk_register_function("open" , o) + exprtk_register_function("close" , c) + exprtk_register_function("write" , w) + exprtk_register_function("read" , r) + exprtk_register_function("getline" , g) + exprtk_register_function("eof" , e) + #undef exprtk_register_function + + return true; + } + }; + + } // namespace exprtk::rtl::io::file + } // namespace exprtk::rtl::io + } // namespace exprtk::rtl +} // namespace exprtk +#endif + +#ifndef exprtk_disable_rtl_vecops +namespace exprtk +{ + namespace rtl { namespace vecops { + + namespace helper + { + template + inline bool invalid_range(const Vector& v, const std::size_t r0, const std::size_t r1) + { + if (r0 > (v.size() - 1)) + return true; + else if (r1 > (v.size() - 1)) + return true; + else if (r1 < r0) + return true; + else + return false; + } + + template + struct load_vector_range + { + typedef typename exprtk::igeneric_function igfun_t; + typedef typename igfun_t::parameter_list_t parameter_list_t; + typedef typename igfun_t::generic_type generic_type; + typedef typename generic_type::scalar_view scalar_t; + typedef typename generic_type::vector_view vector_t; + + static inline bool process(parameter_list_t& parameters, + std::size_t& r0, std::size_t& r1, + const std::size_t& r0_prmidx, + const std::size_t& r1_prmidx, + const std::size_t vec_idx = 0) + { + if (r0_prmidx >= parameters.size()) + return false; + + if (r1_prmidx >= parameters.size()) + return false; + + if (!scalar_t(parameters[r0_prmidx]).to_uint(r0)) + return false; + + if (!scalar_t(parameters[r1_prmidx]).to_uint(r1)) + return false; + + return !invalid_range(vector_t(parameters[vec_idx]), r0, r1); + } + }; + } + + namespace details + { + template + inline void kahan_sum(T& sum, T& error, const T v) + { + const T x = v - error; + const T y = sum + x; + error = (y - sum) - x; + sum = y; + } + + } // namespace exprtk::rtl::details + + template + class all_true exprtk_final : public exprtk::igeneric_function + { + public: + + typedef typename exprtk::igeneric_function igfun_t; + typedef typename igfun_t::parameter_list_t parameter_list_t; + typedef typename igfun_t::generic_type generic_type; + typedef typename generic_type::scalar_view scalar_t; + typedef typename generic_type::vector_view vector_t; + + using igfun_t::operator(); + + all_true() + : exprtk::igeneric_function("V|VTT|T*") + /* + Overloads: + 0. V - vector + 1. VTT - vector, r0, r1 + 2. T* - T....T + */ + {} + + inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override + { + if (2 == ps_index) + { + for (std::size_t i = 0; i < parameters.size(); ++i) + { + if (scalar_t(parameters[i])() == T(0)) + { + return T(0); + } + } + } + else + { + const vector_t vec(parameters[0]); + + std::size_t r0 = 0; + std::size_t r1 = vec.size() - 1; + + if ( + (1 == ps_index) && + !helper::load_vector_range::process(parameters, r0, r1, 1, 2, 0) + ) + { + return std::numeric_limits::quiet_NaN(); + } + + for (std::size_t i = r0; i <= r1; ++i) + { + if (vec[i] == T(0)) + { + return T(0); + } + } + } + + return T(1); + } + }; + + template + class all_false exprtk_final : public exprtk::igeneric_function + { + public: + + typedef typename exprtk::igeneric_function igfun_t; + typedef typename igfun_t::parameter_list_t parameter_list_t; + typedef typename igfun_t::generic_type generic_type; + typedef typename generic_type::scalar_view scalar_t; + typedef typename generic_type::vector_view vector_t; + + using igfun_t::operator(); + + all_false() + : exprtk::igeneric_function("V|VTT|T*") + /* + Overloads: + 0. V - vector + 1. VTT - vector, r0, r1 + 2. T* - T....T + */ + {} + + inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override + { + if (2 == ps_index) + { + for (std::size_t i = 0; i < parameters.size(); ++i) + { + if (scalar_t(parameters[i])() != T(0)) + { + return T(0); + } + } + } + else + { + const vector_t vec(parameters[0]); + + std::size_t r0 = 0; + std::size_t r1 = vec.size() - 1; + + if ( + (1 == ps_index) && + !helper::load_vector_range::process(parameters, r0, r1, 1, 2, 0) + ) + { + return std::numeric_limits::quiet_NaN(); + } + + for (std::size_t i = r0; i <= r1; ++i) + { + if (vec[i] != T(0)) + { + return T(0); + } + } + } + + return T(1); + } + }; + + template + class any_true exprtk_final : public exprtk::igeneric_function + { + public: + + typedef typename exprtk::igeneric_function igfun_t; + typedef typename igfun_t::parameter_list_t parameter_list_t; + typedef typename igfun_t::generic_type generic_type; + typedef typename generic_type::scalar_view scalar_t; + typedef typename generic_type::vector_view vector_t; + + using igfun_t::operator(); + + any_true() + : exprtk::igeneric_function("V|VTT|T*") + /* + Overloads: + 0. V - vector + 1. VTT - vector, r0, r1 + 2. T* - T....T + */ + {} + + inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override + { + if (2 == ps_index) + { + for (std::size_t i = 0; i < parameters.size(); ++i) + { + if (scalar_t(parameters[i])() != T(0)) + { + return T(1); + } + } + } + else + { + const vector_t vec(parameters[0]); + + std::size_t r0 = 0; + std::size_t r1 = vec.size() - 1; + + if ( + (1 == ps_index) && + !helper::load_vector_range::process(parameters, r0, r1, 1, 2, 0) + ) + { + return std::numeric_limits::quiet_NaN(); + } + + for (std::size_t i = r0; i <= r1; ++i) + { + if (vec[i] != T(0)) + { + return T(1); + } + } + } + + return T(0); + } + }; + + template + class any_false exprtk_final : public exprtk::igeneric_function + { + public: + + typedef typename exprtk::igeneric_function igfun_t; + typedef typename igfun_t::parameter_list_t parameter_list_t; + typedef typename igfun_t::generic_type generic_type; + typedef typename generic_type::scalar_view scalar_t; + typedef typename generic_type::vector_view vector_t; + + using igfun_t::operator(); + + any_false() + : exprtk::igeneric_function("V|VTT|T*") + /* + Overloads: + 0. V - vector + 1. VTT - vector, r0, r1 + 2. T* - T....T + */ + {} + + inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override + { + if (2 == ps_index) + { + for (std::size_t i = 0; i < parameters.size(); ++i) + { + if (scalar_t(parameters[i])() == T(0)) + { + return T(1); + } + } + } + else + { + const vector_t vec(parameters[0]); + + std::size_t r0 = 0; + std::size_t r1 = vec.size() - 1; + + if ( + (1 == ps_index) && + !helper::load_vector_range::process(parameters, r0, r1, 1, 2, 0) + ) + { + return std::numeric_limits::quiet_NaN(); + } + + for (std::size_t i = r0; i <= r1; ++i) + { + if (vec[i] == T(0)) + { + return T(1); + } + } + } + + return T(0); + } + }; + + template + class count exprtk_final : public exprtk::igeneric_function + { + public: + + typedef typename exprtk::igeneric_function igfun_t; + typedef typename igfun_t::parameter_list_t parameter_list_t; + typedef typename igfun_t::generic_type generic_type; + typedef typename generic_type::scalar_view scalar_t; + typedef typename generic_type::vector_view vector_t; + + using igfun_t::operator(); + + count() + : exprtk::igeneric_function("V|VTT|T*") + /* + Overloads: + 0. V - vector + 1. VTT - vector, r0, r1 + 2. T* - T....T + */ + {} + + inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override + { + std::size_t cnt = 0; + + if (2 == ps_index) + { + for (std::size_t i = 0; i < parameters.size(); ++i) + { + if (scalar_t(parameters[i])() != T(0)) ++cnt; + } + } + else + { + const vector_t vec(parameters[0]); + + std::size_t r0 = 0; + std::size_t r1 = vec.size() - 1; + + if ( + (1 == ps_index) && + !helper::load_vector_range::process(parameters, r0, r1, 1, 2, 0) + ) + { + return std::numeric_limits::quiet_NaN(); + } + + for (std::size_t i = r0; i <= r1; ++i) + { + if (vec[i] != T(0)) ++cnt; + } + } + + return T(cnt); + } + }; + + template + class copy exprtk_final : public exprtk::igeneric_function + { + public: + + typedef typename exprtk::igeneric_function igfun_t; + typedef typename igfun_t::parameter_list_t parameter_list_t; + typedef typename igfun_t::generic_type generic_type; + typedef typename generic_type::scalar_view scalar_t; + typedef typename generic_type::vector_view vector_t; + + using igfun_t::operator(); + + copy() + : exprtk::igeneric_function("VV|VTTVTT") + /* + Overloads: + 0. VV - x(vector), y(vector) + 1. VTTVTT - x(vector), xr0, xr1, y(vector), yr0, yr1, + */ + {} + + inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override + { + const vector_t x(parameters[0]); + vector_t y(parameters[(0 == ps_index) ? 1 : 3]); + + std::size_t xr0 = 0; + std::size_t xr1 = x.size() - 1; + + std::size_t yr0 = 0; + std::size_t yr1 = y.size() - 1; + + if (1 == ps_index) + { + if ( + !helper::load_vector_range::process(parameters, xr0, xr1, 1, 2, 0) || + !helper::load_vector_range::process(parameters, yr0, yr1, 4, 5, 3) + ) + return T(0); + } + + const std::size_t n = std::min(xr1 - xr0 + 1, yr1 - yr0 + 1); + + std::copy( + x.begin() + xr0, + x.begin() + xr0 + n, + y.begin() + yr0); + + return T(n); + } + }; + + template + class rol exprtk_final : public exprtk::igeneric_function + { + public: + + typedef typename exprtk::igeneric_function igfun_t; + typedef typename igfun_t::parameter_list_t parameter_list_t; + typedef typename igfun_t::generic_type generic_type; + typedef typename generic_type::scalar_view scalar_t; + typedef typename generic_type::vector_view vector_t; + + using igfun_t::operator(); + + rol() + : exprtk::igeneric_function("VT|VTTT") + /* + Overloads: + 0. VT - vector, N + 1. VTTT - vector, N, r0, r1 + */ + {} + + inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override + { + vector_t vec(parameters[0]); + + std::size_t n = 0; + std::size_t r0 = 0; + std::size_t r1 = vec.size() - 1; + + if (!scalar_t(parameters[1]).to_uint(n)) + return T(0); + + if ( + (1 == ps_index) && + !helper::load_vector_range::process(parameters, r0, r1, 2, 3, 0) + ) + return T(0); + + const std::size_t dist = r1 - r0 + 1; + const std::size_t shift = n % dist; + + std::rotate( + vec.begin() + r0, + vec.begin() + r0 + shift, + vec.begin() + r1 + 1); + + return T(1); + } + }; + + template + class ror exprtk_final : public exprtk::igeneric_function + { + public: + + typedef typename exprtk::igeneric_function igfun_t; + typedef typename igfun_t::parameter_list_t parameter_list_t; + typedef typename igfun_t::generic_type generic_type; + typedef typename generic_type::scalar_view scalar_t; + typedef typename generic_type::vector_view vector_t; + + using igfun_t::operator(); + + ror() + : exprtk::igeneric_function("VT|VTTT") + /* + Overloads: + 0. VT - vector, N + 1. VTTT - vector, N, r0, r1 + */ + {} + + inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override + { + vector_t vec(parameters[0]); + + std::size_t n = 0; + std::size_t r0 = 0; + std::size_t r1 = vec.size() - 1; + + if (!scalar_t(parameters[1]).to_uint(n)) + return T(0); + + if ( + (1 == ps_index) && + !helper::load_vector_range::process(parameters, r0, r1, 2, 3, 0) + ) + return T(0); + + const std::size_t dist = r1 - r0 + 1; + const std::size_t shift = (dist - (n % dist)) % dist; + + std::rotate( + vec.begin() + r0, + vec.begin() + r0 + shift, + vec.begin() + r1 + 1); + + return T(1); + } + }; + + template + class reverse exprtk_final : public exprtk::igeneric_function + { + public: + + typedef typename exprtk::igeneric_function igfun_t; + typedef typename igfun_t::parameter_list_t parameter_list_t; + typedef typename igfun_t::generic_type generic_type; + typedef typename generic_type::scalar_view scalar_t; + typedef typename generic_type::vector_view vector_t; + + using igfun_t::operator(); + + reverse() + : exprtk::igeneric_function("V|VTT") + /* + Overloads: + 0. V - vector + 1. VTT - vector, r0, r1 + */ + {} + + inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override + { + vector_t vec(parameters[0]); + + std::size_t r0 = 0; + std::size_t r1 = vec.size() - 1; + + if ( + (1 == ps_index) && + !helper::load_vector_range::process(parameters, r0, r1, 1, 2, 0) + ) + return T(0); + + std::reverse(vec.begin() + r0, vec.begin() + r1 + 1); + + return T(1); + } + }; + + template + class shift_left exprtk_final : public exprtk::igeneric_function + { + public: + + typedef typename exprtk::igeneric_function igfun_t; + typedef typename igfun_t::parameter_list_t parameter_list_t; + typedef typename igfun_t::generic_type generic_type; + typedef typename generic_type::scalar_view scalar_t; + typedef typename generic_type::vector_view vector_t; + + using igfun_t::operator(); + + shift_left() + : exprtk::igeneric_function("VT|VTTT") + /* + Overloads: + 0. VT - vector, N + 1. VTTT - vector, N, r0, r1 + */ + {} + + inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override + { + vector_t vec(parameters[0]); + + std::size_t n = 0; + std::size_t r0 = 0; + std::size_t r1 = vec.size() - 1; + + if (!scalar_t(parameters[1]).to_uint(n)) + return T(0); + + if ( + (1 == ps_index) && + !helper::load_vector_range::process(parameters, r0, r1, 2, 3, 0) + ) + return T(0); + + const std::size_t dist = r1 - r0 + 1; + + if (n > dist) + return T(0); + + std::rotate( + vec.begin() + r0, + vec.begin() + r0 + n, + vec.begin() + r1 + 1); + + for (std::size_t i = r1 - n + 1ULL; i <= r1; ++i) + { + vec[i] = T(0); + } + + return T(1); + } + }; + + template + class shift_right exprtk_final : public exprtk::igeneric_function + { + public: + + typedef typename exprtk::igeneric_function igfun_t; + typedef typename igfun_t::parameter_list_t parameter_list_t; + typedef typename igfun_t::generic_type generic_type; + typedef typename generic_type::scalar_view scalar_t; + typedef typename generic_type::vector_view vector_t; + + using igfun_t::operator(); + + shift_right() + : exprtk::igeneric_function("VT|VTTT") + /* + Overloads: + 0. VT - vector, N + 1. VTTT - vector, N, r0, r1 + */ + {} + + inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override + { + vector_t vec(parameters[0]); + + std::size_t n = 0; + std::size_t r0 = 0; + std::size_t r1 = vec.size() - 1; + + if (!scalar_t(parameters[1]).to_uint(n)) + return T(0); + + if ( + (1 == ps_index) && + !helper::load_vector_range::process(parameters, r0, r1, 2, 3, 0) + ) + return T(0); + + const std::size_t dist = r1 - r0 + 1; + + if (n > dist) + return T(0); + + const std::size_t shift = (dist - (n % dist)) % dist; + + std::rotate( + vec.begin() + r0, + vec.begin() + r0 + shift, + vec.begin() + r1 + 1); + + for (std::size_t i = r0; i < r0 + n; ++i) + { + vec[i] = T(0); + } + + return T(1); + } + }; + + template + class sort exprtk_final : public exprtk::igeneric_function + { + public: + + typedef typename exprtk::igeneric_function igfun_t; + typedef typename igfun_t::parameter_list_t parameter_list_t; + typedef typename igfun_t::generic_type generic_type; + typedef typename generic_type::string_view string_t; + typedef typename generic_type::vector_view vector_t; + + using igfun_t::operator(); + + sort() + : exprtk::igeneric_function("V|VTT|VS|VSTT") + /* + Overloads: + 0. V - vector + 1. VTT - vector, r0, r1 + 2. VS - vector, string + 3. VSTT - vector, string, r0, r1 + */ + {} + + inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override + { + vector_t vec(parameters[0]); + + std::size_t r0 = 0; + std::size_t r1 = vec.size() - 1; + + if ((1 == ps_index) && !helper::load_vector_range::process(parameters, r0, r1, 1, 2, 0)) + return T(0); + if ((3 == ps_index) && !helper::load_vector_range::process(parameters, r0, r1, 2, 3, 0)) + return T(0); + + bool ascending = true; + + if ((2 == ps_index) || (3 == ps_index)) + { + if (exprtk::details::imatch(to_str(string_t(parameters[1])),"ascending")) + ascending = true; + else if (exprtk::details::imatch(to_str(string_t(parameters[1])),"descending")) + ascending = false; + else + return T(0); + } + + if (ascending) + std::sort( + vec.begin() + r0, + vec.begin() + r1 + 1, + std::less()); + else + std::sort( + vec.begin() + r0, + vec.begin() + r1 + 1, + std::greater()); + + return T(1); + } + }; + + template + class nthelement exprtk_final : public exprtk::igeneric_function + { + public: + + typedef typename exprtk::igeneric_function igfun_t; + typedef typename igfun_t::parameter_list_t parameter_list_t; + typedef typename igfun_t::generic_type generic_type; + typedef typename generic_type::scalar_view scalar_t; + typedef typename generic_type::vector_view vector_t; + + using igfun_t::operator(); + + nthelement() + : exprtk::igeneric_function("VT|VTTT") + /* + Overloads: + 0. VT - vector, nth-element + 1. VTTT - vector, nth-element, r0, r1 + */ + {} + + inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override + { + vector_t vec(parameters[0]); + + std::size_t n = 0; + std::size_t r0 = 0; + std::size_t r1 = vec.size() - 1; + + if (!scalar_t(parameters[1]).to_uint(n)) + return T(0); + + if ((1 == ps_index) && !helper::load_vector_range::process(parameters, r0, r1, 2, 3, 0)) + { + return std::numeric_limits::quiet_NaN(); + } + + std::nth_element( + vec.begin() + r0, + vec.begin() + r0 + n , + vec.begin() + r1 + 1); + + return T(1); + } + }; + + template + class assign exprtk_final : public exprtk::igeneric_function + { + public: + + typedef typename exprtk::igeneric_function igfun_t; + typedef typename igfun_t::parameter_list_t parameter_list_t; + typedef typename igfun_t::generic_type generic_type; + typedef typename generic_type::scalar_view scalar_t; + typedef typename generic_type::vector_view vector_t; + + using igfun_t::operator(); + + assign() + : exprtk::igeneric_function("VT|VTTT|VTTTT") + /* + Overloads: + 0. VT - vector, V + 1. VTTT - vector, V, r0, r1 + 2. VTTTT - vector, V, r0, r1, SS + */ + {} + + inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override + { + vector_t vec(parameters[0]); + + const T assign_value = scalar_t(parameters[1]); + + const std::size_t step_size = (2 != ps_index) ? 1 : + static_cast(scalar_t(parameters.back())()); + + std::size_t r0 = 0; + std::size_t r1 = vec.size() - 1; + + if ( + ((ps_index == 1) || (ps_index == 2)) && + !helper::load_vector_range::process(parameters, r0, r1, 2, 3, 0) + ) + { + return T(0); + } + + for (std::size_t i = r0; i <= r1; i += step_size) + { + vec[i] = assign_value; + } + + return T(1); + } + }; + + template + class iota exprtk_final : public exprtk::igeneric_function + { + public: + + typedef typename exprtk::igeneric_function igfun_t; + typedef typename igfun_t::parameter_list_t parameter_list_t; + typedef typename igfun_t::generic_type generic_type; + typedef typename generic_type::scalar_view scalar_t; + typedef typename generic_type::vector_view vector_t; + + using igfun_t::operator(); + + iota() + : exprtk::igeneric_function("VTT|VT|VTTTT|VTTT") + /* + Overloads: + 0. VTT - vector, SV, SS + 1. VT - vector, SV, SS (+1) + 2. VTTT - vector, r0, r1, SV, SS + 3. VTT - vector, r0, r1, SV, SS (+1) + + Where: + 1. SV - Start value + 2. SS - Step size + */ + {} + + inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override + { + vector_t vec(parameters[0]); + + const T start_value = (ps_index <= 1) ? + scalar_t(parameters[1]) : + scalar_t(parameters[3]) ; + + const T step_size = ((0 == ps_index) || (2 == ps_index)) ? + scalar_t(parameters.back())() : + T(1) ; + + std::size_t r0 = 0; + std::size_t r1 = vec.size() - 1; + + if ( + ((ps_index == 2) || (ps_index == 3)) && + !helper::load_vector_range::process(parameters, r0, r1, 1, 2, 0) + ) + { + return T(0); + } + + for (std::size_t i = r0; i <= r1; ++i) + { + vec[i] = start_value + ((i - r0) * step_size); + } + + return T(1); + } + }; + + template + class sumk exprtk_final : public exprtk::igeneric_function + { + public: + + typedef typename exprtk::igeneric_function igfun_t; + typedef typename igfun_t::parameter_list_t parameter_list_t; + typedef typename igfun_t::generic_type generic_type; + typedef typename generic_type::scalar_view scalar_t; + typedef typename generic_type::vector_view vector_t; + + using igfun_t::operator(); + + sumk() + : exprtk::igeneric_function("V|VTT|VTTT") + /* + Overloads: + 0. V - vector + 1. VTT - vector, r0, r1 + 2. VTTT - vector, r0, r1, stride + */ + {} + + inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override + { + const vector_t vec(parameters[0]); + + const std::size_t stride = (2 != ps_index) ? 1 : + static_cast(scalar_t(parameters[3])()); + + std::size_t r0 = 0; + std::size_t r1 = vec.size() - 1; + + if ( + ((1 == ps_index) || (2 == ps_index)) && + !helper::load_vector_range::process(parameters, r0, r1, 1, 2, 0) + ) + { + return std::numeric_limits::quiet_NaN(); + } + + T result = T(0); + T error = T(0); + + for (std::size_t i = r0; i <= r1; i += stride) + { + details::kahan_sum(result, error, vec[i]); + } + + return result; + } + }; + + template + class axpy exprtk_final : public exprtk::igeneric_function + { + public: + + typedef typename exprtk::igeneric_function igfun_t; + typedef typename igfun_t::parameter_list_t parameter_list_t; + typedef typename igfun_t::generic_type generic_type; + typedef typename generic_type::scalar_view scalar_t; + typedef typename generic_type::vector_view vector_t; + + using igfun_t::operator(); + + axpy() + : exprtk::igeneric_function("TVV|TVVTT") + /* + y <- ax + y + Overloads: + 0. TVV - a, x(vector), y(vector) + 1. TVVTT - a, x(vector), y(vector), r0, r1 + */ + {} + + inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override + { + const vector_t x(parameters[1]); + vector_t y(parameters[2]); + + std::size_t r0 = 0; + std::size_t r1 = std::min(x.size(),y.size()) - 1; + + if ((1 == ps_index) && !helper::load_vector_range::process(parameters, r0, r1, 3, 4, 1)) + return std::numeric_limits::quiet_NaN(); + else if (helper::invalid_range(y, r0, r1)) + return std::numeric_limits::quiet_NaN(); + + const T a = scalar_t(parameters[0])(); + + for (std::size_t i = r0; i <= r1; ++i) + { + y[i] = (a * x[i]) + y[i]; + } + + return T(1); + } + }; + + template + class axpby exprtk_final : public exprtk::igeneric_function + { + public: + + typedef typename exprtk::igeneric_function igfun_t; + typedef typename igfun_t::parameter_list_t parameter_list_t; + typedef typename igfun_t::generic_type generic_type; + typedef typename generic_type::scalar_view scalar_t; + typedef typename generic_type::vector_view vector_t; + + using igfun_t::operator(); + + axpby() + : exprtk::igeneric_function("TVTV|TVTVTT") + /* + y <- ax + by + Overloads: + 0. TVTV - a, x(vector), b, y(vector) + 1. TVTVTT - a, x(vector), b, y(vector), r0, r1 + */ + {} + + inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override + { + const vector_t x(parameters[1]); + vector_t y(parameters[3]); + + std::size_t r0 = 0; + std::size_t r1 = std::min(x.size(),y.size()) - 1; + + if ((1 == ps_index) && !helper::load_vector_range::process(parameters, r0, r1, 4, 5, 1)) + return std::numeric_limits::quiet_NaN(); + else if (helper::invalid_range(y, r0, r1)) + return std::numeric_limits::quiet_NaN(); + + const T a = scalar_t(parameters[0])(); + const T b = scalar_t(parameters[2])(); + + for (std::size_t i = r0; i <= r1; ++i) + { + y[i] = (a * x[i]) + (b * y[i]); + } + + return T(1); + } + }; + + template + class axpyz exprtk_final : public exprtk::igeneric_function + { + public: + + typedef typename exprtk::igeneric_function igfun_t; + typedef typename igfun_t::parameter_list_t parameter_list_t; + typedef typename igfun_t::generic_type generic_type; + typedef typename generic_type::scalar_view scalar_t; + typedef typename generic_type::vector_view vector_t; + + using igfun_t::operator(); + + axpyz() + : exprtk::igeneric_function("TVVV|TVVVTT") + /* + z <- ax + y + Overloads: + 0. TVVV - a, x(vector), y(vector), z(vector) + 1. TVVVTT - a, x(vector), y(vector), z(vector), r0, r1 + */ + {} + + inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override + { + const vector_t x(parameters[1]); + const vector_t y(parameters[2]); + vector_t z(parameters[3]); + + std::size_t r0 = 0; + std::size_t r1 = std::min(x.size(),y.size()) - 1; + + if ((1 == ps_index) && !helper::load_vector_range::process(parameters, r0, r1, 4, 5, 1)) + return std::numeric_limits::quiet_NaN(); + else if (helper::invalid_range(y, r0, r1)) + return std::numeric_limits::quiet_NaN(); + else if (helper::invalid_range(z, r0, r1)) + return std::numeric_limits::quiet_NaN(); + + const T a = scalar_t(parameters[0])(); + + for (std::size_t i = r0; i <= r1; ++i) + { + z[i] = (a * x[i]) + y[i]; + } + + return T(1); + } + }; + + template + class axpbyz exprtk_final : public exprtk::igeneric_function + { + public: + + typedef typename exprtk::igeneric_function igfun_t; + typedef typename igfun_t::parameter_list_t parameter_list_t; + typedef typename igfun_t::generic_type generic_type; + typedef typename generic_type::scalar_view scalar_t; + typedef typename generic_type::vector_view vector_t; + + using igfun_t::operator(); + + axpbyz() + : exprtk::igeneric_function("TVTVV|TVTVVTT") + /* + z <- ax + by + Overloads: + 0. TVTVV - a, x(vector), b, y(vector), z(vector) + 1. TVTVVTT - a, x(vector), b, y(vector), z(vector), r0, r1 + */ + {} + + inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override + { + const vector_t x(parameters[1]); + const vector_t y(parameters[3]); + vector_t z(parameters[4]); + + std::size_t r0 = 0; + std::size_t r1 = std::min(x.size(),y.size()) - 1; + + if ((1 == ps_index) && !helper::load_vector_range::process(parameters, r0, r1, 5, 6, 1)) + return std::numeric_limits::quiet_NaN(); + else if (helper::invalid_range(y, r0, r1)) + return std::numeric_limits::quiet_NaN(); + else if (helper::invalid_range(z, r0, r1)) + return std::numeric_limits::quiet_NaN(); + + const T a = scalar_t(parameters[0])(); + const T b = scalar_t(parameters[2])(); + + for (std::size_t i = r0; i <= r1; ++i) + { + z[i] = (a * x[i]) + (b * y[i]); + } + + return T(1); + } + }; + + template + class axpbsy exprtk_final : public exprtk::igeneric_function + { + public: + + typedef typename exprtk::igeneric_function igfun_t; + typedef typename igfun_t::parameter_list_t parameter_list_t; + typedef typename igfun_t::generic_type generic_type; + typedef typename generic_type::scalar_view scalar_t; + typedef typename generic_type::vector_view vector_t; + + using igfun_t::operator(); + + axpbsy() + : exprtk::igeneric_function("TVTTV|TVTTVTT") + /* + y <- ax + by + Overloads: + 0. TVTVV - a, x(vector), b, shift, y(vector), z(vector) + 1. TVTVVTT - a, x(vector), b, shift, y(vector), z(vector), r0, r1 + */ + {} + + inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override + { + const vector_t x(parameters[1]); + vector_t y(parameters[4]); + + std::size_t r0 = 0; + std::size_t r1 = std::min(x.size(),y.size()) - 1; + + if ((1 == ps_index) && !helper::load_vector_range::process(parameters, r0, r1, 5, 6, 1)) + return std::numeric_limits::quiet_NaN(); + else if (helper::invalid_range(y, r0, r1)) + return std::numeric_limits::quiet_NaN(); + + const T a = scalar_t(parameters[0])(); + const T b = scalar_t(parameters[2])(); + + const std::size_t s = static_cast(scalar_t(parameters[3])()); + + for (std::size_t i = r0; i <= r1; ++i) + { + y[i] = (a * x[i]) + (b * y[i + s]); + } + + return T(1); + } + }; + + template + class axpbsyz exprtk_final : public exprtk::igeneric_function + { + public: + + typedef typename exprtk::igeneric_function igfun_t; + typedef typename igfun_t::parameter_list_t parameter_list_t; + typedef typename igfun_t::generic_type generic_type; + typedef typename generic_type::scalar_view scalar_t; + typedef typename generic_type::vector_view vector_t; + + using igfun_t::operator(); + + axpbsyz() + : exprtk::igeneric_function("TVTTVV|TVTTVVTT") + /* + z <- ax + by + Overloads: + 0. TVTVV - a, x(vector), b, shift, y(vector), z(vector) + 1. TVTVVTT - a, x(vector), b, shift, y(vector), z(vector), r0, r1 + */ + {} + + inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override + { + const vector_t x(parameters[1]); + const vector_t y(parameters[4]); + vector_t z(parameters[5]); + + std::size_t r0 = 0; + std::size_t r1 = std::min(x.size(),y.size()) - 1; + + if ((1 == ps_index) && !helper::load_vector_range::process(parameters, r0, r1, 6, 7, 1)) + return std::numeric_limits::quiet_NaN(); + else if (helper::invalid_range(y, r0, r1)) + return std::numeric_limits::quiet_NaN(); + else if (helper::invalid_range(z, r0, r1)) + return std::numeric_limits::quiet_NaN(); + + const T a = scalar_t(parameters[0])(); + const T b = scalar_t(parameters[2])(); + + const std::size_t s = static_cast(scalar_t(parameters[3])()); + + for (std::size_t i = r0; i <= r1; ++i) + { + z[i] = (a * x[i]) + (b * y[i + s]); + } + + return T(1); + } + }; + + template + class axpbz exprtk_final : public exprtk::igeneric_function + { + public: + + typedef typename exprtk::igeneric_function igfun_t; + typedef typename igfun_t::parameter_list_t parameter_list_t; + typedef typename igfun_t::generic_type generic_type; + typedef typename generic_type::scalar_view scalar_t; + typedef typename generic_type::vector_view vector_t; + + using igfun_t::operator(); + + axpbz() + : exprtk::igeneric_function("TVTV|TVTVTT") + /* + z <- ax + b + Overloads: + 0. TVTV - a, x(vector), b, z(vector) + 1. TVTVTT - a, x(vector), b, z(vector), r0, r1 + */ + {} + + inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override + { + const vector_t x(parameters[1]); + vector_t z(parameters[3]); + + std::size_t r0 = 0; + std::size_t r1 = x.size() - 1; + + if ((1 == ps_index) && !helper::load_vector_range::process(parameters, r0, r1, 4, 5, 1)) + return std::numeric_limits::quiet_NaN(); + else if (helper::invalid_range(z, r0, r1)) + return std::numeric_limits::quiet_NaN(); + + const T a = scalar_t(parameters[0])(); + const T b = scalar_t(parameters[2])(); + + for (std::size_t i = r0; i <= r1; ++i) + { + z[i] = (a * x[i]) + b; + } + + return T(1); + } + }; + + template + class diff exprtk_final : public exprtk::igeneric_function + { + public: + + typedef typename exprtk::igeneric_function igfun_t; + typedef typename igfun_t::parameter_list_t parameter_list_t; + typedef typename igfun_t::generic_type generic_type; + typedef typename generic_type::scalar_view scalar_t; + typedef typename generic_type::vector_view vector_t; + + using igfun_t::operator(); + + diff() + : exprtk::igeneric_function("VV|VVT") + /* + x_(i - stride) - x_i + Overloads: + 0. VV - x(vector), y(vector) + 1. VVT - x(vector), y(vector), stride + */ + {} + + inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override + { + const vector_t x(parameters[0]); + vector_t y(parameters[1]); + + const std::size_t r0 = 0; + const std::size_t r1 = std::min(x.size(),y.size()) - 1; + + const std::size_t stride = (1 != ps_index) ? 1 : + std::min(r1,static_cast(scalar_t(parameters[2])())); + + for (std::size_t i = 0; i < stride; ++i) + { + y[i] = std::numeric_limits::quiet_NaN(); + } + + for (std::size_t i = (r0 + stride); i <= r1; ++i) + { + y[i] = x[i] - x[i - stride]; + } + + return T(1); + } + }; + + template + class dot exprtk_final : public exprtk::igeneric_function + { + public: + + typedef typename exprtk::igeneric_function igfun_t; + typedef typename igfun_t::parameter_list_t parameter_list_t; + typedef typename igfun_t::generic_type generic_type; + typedef typename generic_type::scalar_view scalar_t; + typedef typename generic_type::vector_view vector_t; + + using igfun_t::operator(); + + dot() + : exprtk::igeneric_function("VV|VVTT") + /* + Overloads: + 0. VV - x(vector), y(vector) + 1. VVTT - x(vector), y(vector), r0, r1 + */ + {} + + inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override + { + const vector_t x(parameters[0]); + const vector_t y(parameters[1]); + + std::size_t r0 = 0; + std::size_t r1 = std::min(x.size(),y.size()) - 1; + + if ((1 == ps_index) && !helper::load_vector_range::process(parameters, r0, r1, 2, 3, 0)) + return std::numeric_limits::quiet_NaN(); + else if (helper::invalid_range(y, r0, r1)) + return std::numeric_limits::quiet_NaN(); + + T result = T(0); + + for (std::size_t i = r0; i <= r1; ++i) + { + result += (x[i] * y[i]); + } + + return result; + } + }; + + template + class dotk exprtk_final : public exprtk::igeneric_function + { + public: + + typedef typename exprtk::igeneric_function igfun_t; + typedef typename igfun_t::parameter_list_t parameter_list_t; + typedef typename igfun_t::generic_type generic_type; + typedef typename generic_type::scalar_view scalar_t; + typedef typename generic_type::vector_view vector_t; + + using igfun_t::operator(); + + dotk() + : exprtk::igeneric_function("VV|VVTT") + /* + Overloads: + 0. VV - x(vector), y(vector) + 1. VVTT - x(vector), y(vector), r0, r1 + */ + {} + + inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override + { + const vector_t x(parameters[0]); + const vector_t y(parameters[1]); + + std::size_t r0 = 0; + std::size_t r1 = std::min(x.size(),y.size()) - 1; + + if ((1 == ps_index) && !helper::load_vector_range::process(parameters, r0, r1, 2, 3, 0)) + return std::numeric_limits::quiet_NaN(); + else if (helper::invalid_range(y, r0, r1)) + return std::numeric_limits::quiet_NaN(); + + T result = T(0); + T error = T(0); + + for (std::size_t i = r0; i <= r1; ++i) + { + details::kahan_sum(result, error, (x[i] * y[i])); + } + + return result; + } + }; + + template + class threshold_below exprtk_final : public exprtk::igeneric_function + { + public: + + typedef typename exprtk::igeneric_function igfun_t; + typedef typename igfun_t::parameter_list_t parameter_list_t; + typedef typename igfun_t::generic_type generic_type; + typedef typename generic_type::scalar_view scalar_t; + typedef typename generic_type::vector_view vector_t; + + using igfun_t::operator(); + + threshold_below() + : exprtk::igeneric_function("VTT|VTTTT") + /* + Overloads: + 0. VTT - vector, TV, SV + 1. VTTTT - vector, r0, r1, TV, SV + + Where: + TV - Threshold value + SV - Snap-to value + */ + {} + + inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override + { + vector_t vec(parameters[0]); + + const T threshold_value = (0 == ps_index) ? + scalar_t(parameters[1]) : + scalar_t(parameters[3]) ; + + const T snap_value = scalar_t(parameters.back()); + + std::size_t r0 = 0; + std::size_t r1 = vec.size() - 1; + + if ( + (1 == ps_index) && + !helper::load_vector_range::process(parameters, r0, r1, 1, 2, 0) + ) + { + return T(0); + } + + for (std::size_t i = r0; i <= r1; ++i) + { + if (vec[i] < threshold_value) + { + vec[i] = snap_value; + } + } + + return T(1); + } + }; + + template + class threshold_above exprtk_final : public exprtk::igeneric_function + { + public: + + typedef typename exprtk::igeneric_function igfun_t; + typedef typename igfun_t::parameter_list_t parameter_list_t; + typedef typename igfun_t::generic_type generic_type; + typedef typename generic_type::scalar_view scalar_t; + typedef typename generic_type::vector_view vector_t; + + using igfun_t::operator(); + + threshold_above() + : exprtk::igeneric_function("VTT|VTTTT") + /* + Overloads: + 0. VTT - vector, TV, SV + 1. VTTTT - vector, r0, r1, TV, SV + + Where: + TV - Threshold value + SV - Snap-to value + */ + {} + + inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override + { + vector_t vec(parameters[0]); + + const T threshold_value = (0 == ps_index) ? + scalar_t(parameters[1]) : + scalar_t(parameters[3]) ; + + const T snap_value = scalar_t(parameters.back()); + + std::size_t r0 = 0; + std::size_t r1 = vec.size() - 1; + + if ( + (1 == ps_index) && + !helper::load_vector_range::process(parameters, r0, r1, 1, 2, 0) + ) + { + return T(0); + } + + for (std::size_t i = r0; i <= r1; ++i) + { + if (vec[i] > threshold_value) + { + vec[i] = snap_value; + } + } + + return T(1); + } + }; + + template + struct package + { + all_true at; + all_false af; + any_true nt; + any_false nf; + count c; + copy cp; + rol rl; + ror rr; + reverse rev; + shift_left sl; + shift_right sr; + sort st; + nthelement ne; + assign an; + iota ia; + sumk sk; + axpy b1_axpy; + axpby b1_axpby; + axpyz b1_axpyz; + axpbyz b1_axpbyz; + axpbsy b1_axpbsy; + axpbsyz b1_axpbsyz; + axpbz b1_axpbz; + diff df; + dot dt; + dotk dtk; + threshold_above ta; + threshold_below tb; + + bool register_package(exprtk::symbol_table& symtab) + { + #define exprtk_register_function(FunctionName, FunctionType) \ + if (!symtab.add_function(FunctionName,FunctionType)) \ + { \ + exprtk_debug(( \ + "exprtk::rtl::vecops::register_package - Failed to add function: %s\n", \ + FunctionName)); \ + return false; \ + } \ + + exprtk_register_function("all_true" , at ) + exprtk_register_function("all_false" , af ) + exprtk_register_function("any_true" , nt ) + exprtk_register_function("any_false" , nf ) + exprtk_register_function("count" , c ) + exprtk_register_function("copy" , cp ) + exprtk_register_function("rotate_left" , rl ) + exprtk_register_function("rol" , rl ) + exprtk_register_function("rotate_right" , rr ) + exprtk_register_function("ror" , rr ) + exprtk_register_function("reverse" , rev ) + exprtk_register_function("shftl" , sl ) + exprtk_register_function("shftr" , sr ) + exprtk_register_function("sort" , st ) + exprtk_register_function("nth_element" , ne ) + exprtk_register_function("assign" , an ) + exprtk_register_function("iota" , ia ) + exprtk_register_function("sumk" , sk ) + exprtk_register_function("axpy" , b1_axpy ) + exprtk_register_function("axpby" , b1_axpby ) + exprtk_register_function("axpyz" , b1_axpyz ) + exprtk_register_function("axpbyz" , b1_axpbyz ) + exprtk_register_function("axpbsy" , b1_axpbsy ) + exprtk_register_function("axpbsyz" , b1_axpbsyz) + exprtk_register_function("axpbz" , b1_axpbz ) + exprtk_register_function("diff" , df ) + exprtk_register_function("dot" , dt ) + exprtk_register_function("dotk" , dtk ) + exprtk_register_function("threshold_above" , ta ) + exprtk_register_function("threshold_below" , tb ) + #undef exprtk_register_function + + return true; + } + }; + + } // namespace exprtk::rtl::vecops + } // namespace exprtk::rtl +} // namespace exprtk +#endif + +namespace exprtk +{ + namespace information + { + using ::exprtk::details::char_cptr; + + static char_cptr library = "Mathematical Expression Toolkit"; + static char_cptr version = "2.718281828459045235360287471352662497757" + "24709369995957496696762772407663035354759" + "45713821785251664274274663919320030599218" + "17413596629043572900334295260595630738132"; + static char_cptr date = "20240101"; + static char_cptr min_cpp = "199711L"; + + static inline std::string data() + { + static const std::string info_str = std::string(library) + + std::string(" v") + std::string(version) + + std::string(" (") + date + std::string(")") + + std::string(" (") + min_cpp + std::string(")"); + return info_str; + } + + } // namespace information + + #ifdef exprtk_debug + #undef exprtk_debug + #endif + + #ifdef exprtk_error_location + #undef exprtk_error_location + #endif + + #ifdef exprtk_fallthrough + #undef exprtk_fallthrough + #endif + + #ifdef exprtk_override + #undef exprtk_override + #endif + + #ifdef exprtk_final + #undef exprtk_final + #endif + + #ifdef exprtk_delete + #undef exprtk_delete + #endif + +} // namespace exprtk + +#endif diff --git a/src/common/CalculatorEngineCommon/packages.config b/src/common/CalculatorEngineCommon/packages.config new file mode 100644 index 0000000000..d83224ba6f --- /dev/null +++ b/src/common/CalculatorEngineCommon/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/common/CalculatorEngineCommon/pch.cpp b/src/common/CalculatorEngineCommon/pch.cpp new file mode 100644 index 0000000000..bcb5590be1 --- /dev/null +++ b/src/common/CalculatorEngineCommon/pch.cpp @@ -0,0 +1 @@ +#include "pch.h" diff --git a/src/common/CalculatorEngineCommon/pch.h b/src/common/CalculatorEngineCommon/pch.h new file mode 100644 index 0000000000..21199686d5 --- /dev/null +++ b/src/common/CalculatorEngineCommon/pch.h @@ -0,0 +1,4 @@ +#pragma once +#include +#include +#include diff --git a/src/common/CalculatorEngineCommon/readme.md b/src/common/CalculatorEngineCommon/readme.md new file mode 100644 index 0000000000..1455e8f1aa --- /dev/null +++ b/src/common/CalculatorEngineCommon/readme.md @@ -0,0 +1,29 @@ +# C++/WinRT CalculatorEngine Project Overview + +This project wraps the exprtk expression parsing library with a C++/WinRT component, +making advanced mathematical evaluation capabilities available to Windows applications. +It is designed specifically to provide calculation support for the CmdPal calculator extension. + +## Using exprtk + +This project uses [exprtk](https://github.com/ArashPartow/exprtk) as the +expression parsing and evaluation engine. + +How to use exprtk in this project: +- The exprtk header file (`exprtk.hpp`) is included in the project source. +- You can use exprtk to parse and evaluate mathematical expressions in your + C++ code. For example: + + ```cpp + #include "exprtk.hpp" + exprtk::expression expression; + exprtk::parser parser; + std::string formula = "3 + 4 * 2"; + parser.compile(formula, expression); + double result = expression.value(); + ``` + +How to update exprtk: +1. Download the latest `exprtk.hpp` from the [official repository](https://github.com/ArashPartow/exprtk). +2. Replace the existing `exprtk.hpp` file in the project with the new version. +3. Rebuild the project to ensure compatibility and take advantage of any updates. \ No newline at end of file diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/CalculateEngine.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/CalculateEngine.cs index 0d6f9536db..35e53b0e95 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/CalculateEngine.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/CalculateEngine.cs @@ -6,20 +6,20 @@ using System; using System.Collections.Generic; using System.Globalization; using System.Text.RegularExpressions; - -using Mages.Core; +using CalculatorEngineCommon; +using Windows.Foundation.Collections; namespace Microsoft.CmdPal.Ext.Calc.Helper; public static class CalculateEngine { - private static readonly Engine _magesEngine = new Engine(new Configuration + private static readonly PropertySet _constants = new() { - Scope = new Dictionary - { - { "e", Math.E }, // e is not contained in the default mages engine - }, - }); + { "pi", Math.PI }, + { "e", Math.E }, + }; + + private static readonly Calculator _calculator = new Calculator(_constants); public const int RoundingDigits = 10; @@ -68,28 +68,26 @@ public static class CalculateEngine // Expand conversions between trig units input = CalculateHelper.ExpandTrigConversions(input, trigMode); - var result = _magesEngine.Interpret(input); + var result = _calculator.EvaluateExpression(input); // This could happen for some incorrect queries, like pi(2) - if (result == null) + if (result == "NaN") { error = Properties.Resources.calculator_expression_not_complete; return default; } - result = TransformResult(result); - if (result is string) - { - error = result as string; - return default; - } - - if (string.IsNullOrEmpty(result?.ToString())) + if (string.IsNullOrEmpty(result)) { return default; } var decimalResult = Convert.ToDecimal(result, cultureInfo); + + // Remove trailing zeros from the decimal string representation (e.g., "1.2300" -> "1.23") + // This is necessary because the value extracted from exprtk may contain unnecessary trailing zeros. + var formatted = decimalResult.ToString("G29", cultureInfo); + decimalResult = Convert.ToDecimal(formatted, cultureInfo); var roundedResult = Round(decimalResult); return new CalculateResult() @@ -103,25 +101,4 @@ public static class CalculateEngine { return Math.Round(value, RoundingDigits, MidpointRounding.AwayFromZero); } - - private static dynamic TransformResult(object result) - { - if (result.ToString() == "NaN") - { - return Properties.Resources.calculator_not_a_number; - } - - if (result is Function) - { - return Properties.Resources.calculator_expression_not_complete; - } - - if (result is double[,]) - { - // '[10,10]' is interpreted as array by mages engine - return Properties.Resources.calculator_double_array_returned; - } - - return result; - } } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/NumberTranslator.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/NumberTranslator.cs index 8de77ebdae..6930e2fa23 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/NumberTranslator.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/NumberTranslator.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Globalization; using System.Text; using System.Text.RegularExpressions; @@ -63,46 +64,61 @@ public class NumberTranslator return Translate(input, targetCulture, sourceCulture, splitRegexForTarget); } + private static string ConvertBaseLiteral(string token, CultureInfo cultureTo) + { + var prefixes = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "0x", 16 }, + { "0b", 2 }, + { "0o", 8 }, + }; + + foreach (var (prefix, numberBase) in prefixes) + { + if (token.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) + { + try + { + var num = Convert.ToInt64(token.Substring(prefix.Length), numberBase); + return num.ToString(cultureTo); + } + catch + { + return null; // fallback + } + } + } + + return null; + } + private static string Translate(string input, CultureInfo cultureFrom, CultureInfo cultureTo, Regex splitRegex) { var outputBuilder = new StringBuilder(); - var hexRegex = new Regex(@"(?:(0x[\da-fA-F]+))"); - var hexTokens = hexRegex.Split(input); + // Match numbers in hexadecimal (0x..), binary (0b..), or octal (0o..) format, + // and convert them to decimal form for compatibility with ExprTk (which only supports decimal input). + var baseNumberRegex = new Regex(@"(0[xX][\da-fA-F]+|0[bB][0-9]+|0[oO][0-9]+)"); - foreach (var hexToken in hexTokens) + var tokens = baseNumberRegex.Split(input); + + foreach (var token in tokens) { - if (hexToken.StartsWith("0x", StringComparison.InvariantCultureIgnoreCase)) - { - // Mages engine has issues processing large hex number (larger than 7 hex digits + 0x prefix = 9 characters). So we convert it to decimal and pass it to the engine. - if (hexToken.Length > 9) - { - try - { - var num = Convert.ToInt64(hexToken, 16); - var numStr = num.ToString(cultureFrom); - outputBuilder.Append(numStr); - } - catch (Exception) - { - outputBuilder.Append(hexToken); - } - } - else - { - outputBuilder.Append(hexToken); - } + // Currently, we only convert base literals (hexadecimal, binary, octal) to decimal. + var converted = ConvertBaseLiteral(token, cultureTo); + if (converted != null) + { + outputBuilder.Append(converted); continue; } - var tokens = splitRegex.Split(hexToken); - foreach (var token in tokens) + foreach (var inner in splitRegex.Split(token)) { var leadingZeroCount = 0; // Count leading zero characters. - foreach (var c in token) + foreach (var c in inner) { if (c != '0') { @@ -113,7 +129,7 @@ public class NumberTranslator } // number is all zero characters. no need to add zero characters at the end. - if (token.Length == leadingZeroCount) + if (inner.Length == leadingZeroCount) { leadingZeroCount = 0; } @@ -121,9 +137,9 @@ public class NumberTranslator decimal number; outputBuilder.Append( - decimal.TryParse(token, NumberStyles.Number, cultureFrom, out number) + decimal.TryParse(inner, NumberStyles.Number, cultureFrom, out number) ? (new string('0', leadingZeroCount) + number.ToString(cultureTo)) - : token.Replace(cultureFrom.TextInfo.ListSeparator, cultureTo.TextInfo.ListSeparator)); + : inner.Replace(cultureFrom.TextInfo.ListSeparator, cultureTo.TextInfo.ListSeparator)); } } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/QueryHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/QueryHelper.cs index 7cbdf4035f..df6a8a153d 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/QueryHelper.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/QueryHelper.cs @@ -65,11 +65,6 @@ public static partial class QueryHelper return ResultHelper.CreateResult(result.RoundedResult, inputCulture, outputCulture, query, handleSave); } - catch (Mages.Core.ParseException) - { - // Invalid input - return ErrorHandler.OnError(isFallbackSearch, query, Properties.Resources.calculator_expression_not_complete); - } catch (OverflowException) { // Result to big to convert to decimal diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Microsoft.CmdPal.Ext.Calc.csproj b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Microsoft.CmdPal.Ext.Calc.csproj index 59f5ccbc70..fc84428a76 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Microsoft.CmdPal.Ext.Calc.csproj +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Microsoft.CmdPal.Ext.Calc.csproj @@ -9,15 +9,25 @@ Microsoft.CmdPal.Ext.Calc.pri + + CalculatorEngineCommon + $(OutDir) + + + - - + + + PreserveNewest + + + PreserveNewest + - Resources.resx From ce058f1dc7966e702681580a96b57859f5c9886d Mon Sep 17 00:00:00 2001 From: PesBandi <127593627+PesBandi@users.noreply.github.com> Date: Fri, 13 Jun 2025 12:01:40 +0200 Subject: [PATCH 071/117] [ColorPicker]Add option to choose what clicking individual mouse buttons does (#39025) ## Summary of the Pull Request Enables the users to choose what left, right, and middle click does when color picker is open. ## PR Checklist - [x] **Closes:** #39006 - [x] **Communication:** I've discussed this with core contributors already - [x] **Tests:** All pass - [x] **Localization:** All end user facing strings can be localized - [x] **Dev docs:** No need - [x] **New binaries:** None - [x] **Documentation updated:** No need ## Detailed Description of the Pull Request / Additional comments ![Screenshot of the settings](https://github.com/user-attachments/assets/a3e1349b-6bb9-4e2f-97f3-a5106a7d92ce) Adds option to choose from 3 click behaviors for each standard mouse button: * **Pick color and open editor**: Copies color to clipboard, saves it to the color history and opens the editor * **Pick color and close**: Copies color to clipboard, saves it to the color history and exits * **Close**: Closes color picker without copying the color Pressing Enter or Space does what clicking the primary button would. Left and middle click actions execute on mouse down, right click on mouse up for reasons discussed previously (I can't find the conversation now) Default settings are chosen in such a way that very little or nothing changes by updating. ## Validation Steps Performed Tested: * Migrating settings from v2.0 to v2.1 (v1 to v2.1 not tested) * Settings page displays and saves settings correctly * Default settings load correctly * All three click actions do what they are supposed to * Activation behavior works as expected, doesn't affect click actions --- .github/actions/spell-check/expect.txt | 3 + .../ColorPickerUI/Helpers/AppStateHandler.cs | 17 ++--- .../ColorPickerUI/Mouse/IMouseInfoProvider.cs | 4 +- .../ColorPickerUI/Mouse/MouseHook.cs | 46 +++++++++++--- .../ColorPickerUI/Mouse/MouseInfoProvider.cs | 20 ++++-- .../ColorPickerUI/Settings/IUserSettings.cs | 6 ++ .../ColorPickerUI/Settings/UserSettings.cs | 14 ++++- .../ColorPickerUI/ViewModels/MainViewModel.cs | 63 +++++++++++++------ .../ColorPickerProperties.cs | 14 ++++- .../ColorPickerPropertiesVersion1.cs | 2 +- .../ColorPickerSettings.cs | 19 +++++- .../ColorPickerActivationAction.cs | 7 +-- .../Enumerations/ColorPickerClickAction.cs | 18 ++++++ .../Settings.UI.Library/SettingsUtils.cs | 7 +++ .../SettingsXAML/Views/ColorPickerPage.xaml | 33 +++++++++- .../Settings.UI/Strings/en-us/Resources.resw | 39 +++++++++--- .../ViewModels/ColorPickerViewModel.cs | 45 +++++++++++++ 17 files changed, 291 insertions(+), 66 deletions(-) create mode 100644 src/settings-ui/Settings.UI.Library/Enumerations/ColorPickerClickAction.cs diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 6e3f041fba..1dcfdef0e1 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -910,6 +910,7 @@ metafile mfc Mgmt Microwaved +middleclickaction midl mii mindaro @@ -1251,6 +1252,7 @@ prg prgh prgms pri +primaryclickaction PRINTCLIENT printmanagement prm @@ -1426,6 +1428,7 @@ SDKDDK sdns searchterm SEARCHUI +secondaryclickaction SECONDARYDISPLAY secpol securestring diff --git a/src/modules/colorPicker/ColorPickerUI/Helpers/AppStateHandler.cs b/src/modules/colorPicker/ColorPickerUI/Helpers/AppStateHandler.cs index b3350702c7..705324cb93 100644 --- a/src/modules/colorPicker/ColorPickerUI/Helpers/AppStateHandler.cs +++ b/src/modules/colorPicker/ColorPickerUI/Helpers/AppStateHandler.cs @@ -107,21 +107,14 @@ namespace ColorPicker.Helpers } } - public void OnColorPickerMouseDown() + public void OpenColorEditor() { - if (_userSettings.ActivationAction.Value == ColorPickerActivationAction.OpenColorPickerAndThenEditor || _userSettings.ActivationAction.Value == ColorPickerActivationAction.OpenEditor) + lock (_colorPickerVisibilityLock) { - lock (_colorPickerVisibilityLock) - { - HideColorPicker(); - } + HideColorPicker(); + } - ShowColorPickerEditor(); - } - else - { - EndUserSession(); - } + ShowColorPickerEditor(); } public static void SetTopMost() diff --git a/src/modules/colorPicker/ColorPickerUI/Mouse/IMouseInfoProvider.cs b/src/modules/colorPicker/ColorPickerUI/Mouse/IMouseInfoProvider.cs index 3361b95cb4..51bb35ac51 100644 --- a/src/modules/colorPicker/ColorPickerUI/Mouse/IMouseInfoProvider.cs +++ b/src/modules/colorPicker/ColorPickerUI/Mouse/IMouseInfoProvider.cs @@ -16,10 +16,12 @@ namespace ColorPicker.Mouse // position and bool indicating zoom in or zoom out event EventHandler> OnMouseWheel; - event MouseUpEventHandler OnMouseDown; + event PrimaryMouseDownEventHandler OnPrimaryMouseDown; event SecondaryMouseUpEventHandler OnSecondaryMouseUp; + event MiddleMouseDownEventHandler OnMiddleMouseDown; + System.Windows.Point CurrentPosition { get; } Color CurrentColor { get; } diff --git a/src/modules/colorPicker/ColorPickerUI/Mouse/MouseHook.cs b/src/modules/colorPicker/ColorPickerUI/Mouse/MouseHook.cs index 8476398c99..72fffd1445 100644 --- a/src/modules/colorPicker/ColorPickerUI/Mouse/MouseHook.cs +++ b/src/modules/colorPicker/ColorPickerUI/Mouse/MouseHook.cs @@ -7,17 +7,18 @@ using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows.Input; -using ColorPicker.Helpers; using ManagedCommon; using static ColorPicker.NativeMethods; namespace ColorPicker.Mouse { - public delegate void MouseUpEventHandler(object sender, System.Drawing.Point p); + public delegate void PrimaryMouseDownEventHandler(object sender, IntPtr wParam); public delegate void SecondaryMouseUpEventHandler(object sender, IntPtr wParam); + public delegate void MiddleMouseDownEventHandler(object sender, IntPtr wParam); + internal class MouseHook { [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "Interop object")] @@ -30,23 +31,25 @@ namespace ColorPicker.Mouse private const int WM_RBUTTONUP = 0x0205; [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "Interop object")] private const int WM_RBUTTONDOWN = 0x0204; + [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "Interop object")] + private const int WM_MBUTTONDOWN = 0x0207; private IntPtr _mouseHookHandle; private HookProc _mouseDelegate; - private event MouseUpEventHandler MouseDown; + private event PrimaryMouseDownEventHandler PrimaryMouseDown; - public event MouseUpEventHandler OnMouseDown + public event PrimaryMouseDownEventHandler OnPrimaryMouseDown { add { Subscribe(); - MouseDown += value; + PrimaryMouseDown += value; } remove { - MouseDown -= value; + PrimaryMouseDown -= value; Unsubscribe(); } } @@ -68,6 +71,23 @@ namespace ColorPicker.Mouse } } + private event MiddleMouseDownEventHandler MiddleMouseDown; + + public event MiddleMouseDownEventHandler OnMiddleMouseDown + { + add + { + Subscribe(); + MiddleMouseDown += value; + } + + remove + { + MiddleMouseDown -= value; + Unsubscribe(); + } + } + private event MouseWheelEventHandler MouseWheel; public event MouseWheelEventHandler OnMouseWheel @@ -126,9 +146,9 @@ namespace ColorPicker.Mouse MSLLHOOKSTRUCT mouseHookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT)); if (wParam.ToInt32() == WM_LBUTTONDOWN) { - if (MouseDown != null) + if (PrimaryMouseDown != null) { - MouseDown.Invoke(null, new System.Drawing.Point(mouseHookStruct.pt.x, mouseHookStruct.pt.y)); + PrimaryMouseDown.Invoke(null, wParam); } return new IntPtr(-1); @@ -150,6 +170,16 @@ namespace ColorPicker.Mouse return new IntPtr(-1); } + if (wParam.ToInt32() == WM_MBUTTONDOWN) + { + if (MiddleMouseDown != null) + { + MiddleMouseDown.Invoke(null, wParam); + } + + return new IntPtr(-1); + } + if (wParam.ToInt32() == WM_MOUSEWHEEL) { if (MouseWheel != null) diff --git a/src/modules/colorPicker/ColorPickerUI/Mouse/MouseInfoProvider.cs b/src/modules/colorPicker/ColorPickerUI/Mouse/MouseInfoProvider.cs index c28dcc1ae6..4d6596bc3f 100644 --- a/src/modules/colorPicker/ColorPickerUI/Mouse/MouseInfoProvider.cs +++ b/src/modules/colorPicker/ColorPickerUI/Mouse/MouseInfoProvider.cs @@ -56,10 +56,12 @@ namespace ColorPicker.Mouse public event EventHandler> OnMouseWheel; - public event MouseUpEventHandler OnMouseDown; + public event PrimaryMouseDownEventHandler OnPrimaryMouseDown; public event SecondaryMouseUpEventHandler OnSecondaryMouseUp; + public event MiddleMouseDownEventHandler OnMiddleMouseDown; + public System.Windows.Point CurrentPosition { get @@ -148,9 +150,10 @@ namespace ColorPicker.Mouse _timer.Start(); } - _mouseHook.OnMouseDown += MouseHook_OnMouseDown; + _mouseHook.OnPrimaryMouseDown += MouseHook_OnPrimaryMouseDown; _mouseHook.OnMouseWheel += MouseHook_OnMouseWheel; _mouseHook.OnSecondaryMouseUp += MouseHook_OnSecondaryMouseUp; + _mouseHook.OnMiddleMouseDown += MouseHook_OnMiddleMouseDown; if (_userSettings.ChangeCursor.Value) { @@ -169,10 +172,10 @@ namespace ColorPicker.Mouse OnMouseWheel?.Invoke(this, new Tuple(_previousMousePosition, zoomIn)); } - private void MouseHook_OnMouseDown(object sender, Point p) + private void MouseHook_OnPrimaryMouseDown(object sender, IntPtr wParam) { DisposeHook(); - OnMouseDown?.Invoke(this, p); + OnPrimaryMouseDown?.Invoke(this, wParam); } private void MouseHook_OnSecondaryMouseUp(object sender, IntPtr wParam) @@ -181,6 +184,12 @@ namespace ColorPicker.Mouse OnSecondaryMouseUp?.Invoke(this, wParam); } + private void MouseHook_OnMiddleMouseDown(object sender, IntPtr wParam) + { + DisposeHook(); + OnMiddleMouseDown?.Invoke(this, wParam); + } + private void CopiedColorRepresentation_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { _colorFormatChanged = true; @@ -194,9 +203,10 @@ namespace ColorPicker.Mouse } _previousMousePosition = new System.Windows.Point(-1, 1); - _mouseHook.OnMouseDown -= MouseHook_OnMouseDown; + _mouseHook.OnPrimaryMouseDown -= MouseHook_OnPrimaryMouseDown; _mouseHook.OnMouseWheel -= MouseHook_OnMouseWheel; _mouseHook.OnSecondaryMouseUp -= MouseHook_OnSecondaryMouseUp; + _mouseHook.OnMiddleMouseDown -= MouseHook_OnMiddleMouseDown; if (_userSettings.ChangeCursor.Value) { diff --git a/src/modules/colorPicker/ColorPickerUI/Settings/IUserSettings.cs b/src/modules/colorPicker/ColorPickerUI/Settings/IUserSettings.cs index ab92e64081..170b58c944 100644 --- a/src/modules/colorPicker/ColorPickerUI/Settings/IUserSettings.cs +++ b/src/modules/colorPicker/ColorPickerUI/Settings/IUserSettings.cs @@ -21,6 +21,12 @@ namespace ColorPicker.Settings SettingItem ActivationAction { get; } + SettingItem PrimaryClickAction { get; } + + SettingItem MiddleClickAction { get; } + + SettingItem SecondaryClickAction { get; } + RangeObservableCollection ColorHistory { get; } SettingItem ColorHistoryLimit { get; } diff --git a/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs b/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs index eb6a9db7aa..e15a792259 100644 --- a/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs +++ b/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs @@ -49,7 +49,10 @@ namespace ColorPicker.Settings ChangeCursor = new SettingItem(true); ActivationShortcut = new SettingItem(DefaultActivationShortcut); CopiedColorRepresentation = new SettingItem(ColorRepresentationType.HEX.ToString()); - ActivationAction = new SettingItem(ColorPickerActivationAction.OpenEditor); + ActivationAction = new SettingItem(ColorPickerActivationAction.OpenColorPicker); + PrimaryClickAction = new SettingItem(ColorPickerClickAction.PickColorThenEditor); + MiddleClickAction = new SettingItem(ColorPickerClickAction.PickColorAndClose); + SecondaryClickAction = new SettingItem(ColorPickerClickAction.Close); ColorHistoryLimit = new SettingItem(20); ColorHistory.CollectionChanged += ColorHistory_CollectionChanged; ShowColorName = new SettingItem(false); @@ -78,6 +81,12 @@ namespace ColorPicker.Settings public SettingItem ActivationAction { get; private set; } + public SettingItem PrimaryClickAction { get; private set; } + + public SettingItem MiddleClickAction { get; private set; } + + public SettingItem SecondaryClickAction { get; private set; } + public RangeObservableCollection ColorHistory { get; private set; } = new RangeObservableCollection(); public SettingItem ColorHistoryLimit { get; } @@ -121,6 +130,9 @@ namespace ColorPicker.Settings CopiedColorRepresentation.Value = settings.Properties.CopiedColorRepresentation; CopiedColorRepresentationFormat = new SettingItem(string.Empty); ActivationAction.Value = settings.Properties.ActivationAction; + PrimaryClickAction.Value = settings.Properties.PrimaryClickAction; + MiddleClickAction.Value = settings.Properties.MiddleClickAction; + SecondaryClickAction.Value = settings.Properties.SecondaryClickAction; ColorHistoryLimit.Value = settings.Properties.ColorHistoryLimit; ShowColorName.Value = settings.Properties.ShowColorName; diff --git a/src/modules/colorPicker/ColorPickerUI/ViewModels/MainViewModel.cs b/src/modules/colorPicker/ColorPickerUI/ViewModels/MainViewModel.cs index 518f253456..e4d0a54b44 100644 --- a/src/modules/colorPicker/ColorPickerUI/ViewModels/MainViewModel.cs +++ b/src/modules/colorPicker/ColorPickerUI/ViewModels/MainViewModel.cs @@ -16,6 +16,7 @@ using ColorPicker.Settings; using ColorPicker.ViewModelContracts; using Common.UI; using ManagedCommon; +using Microsoft.PowerToys.Settings.UI.Library.Enumerations; using PowerToys.Interop; namespace ColorPicker.ViewModels @@ -79,9 +80,10 @@ namespace ColorPicker.ViewModels { SetColorDetails(mouseInfoProvider.CurrentColor); mouseInfoProvider.MouseColorChanged += Mouse_ColorChanged; - mouseInfoProvider.OnMouseDown += MouseInfoProvider_OnMouseDown; + mouseInfoProvider.OnPrimaryMouseDown += MouseInfoProvider_OnPrimaryMouseDown; mouseInfoProvider.OnMouseWheel += MouseInfoProvider_OnMouseWheel; mouseInfoProvider.OnSecondaryMouseUp += MouseInfoProvider_OnSecondaryMouseUp; + mouseInfoProvider.OnMiddleMouseDown += MouseInfoProvider_OnMiddleMouseDown; } _userSettings.ShowColorName.PropertyChanged += (s, e) => { OnPropertyChanged(nameof(ShowColorName)); }; @@ -113,7 +115,7 @@ namespace ColorPicker.ViewModels private void AppStateHandler_EnterPressed(object sender, EventArgs e) { - MouseInfoProvider_OnMouseDown(null, default(System.Drawing.Point)); + MouseInfoProvider_OnPrimaryMouseDown(null, default); } /// @@ -167,18 +169,50 @@ namespace ColorPicker.ViewModels SetColorDetails(color); } - /// - /// Tell the color picker that the user have press a mouse button (after release the button) - /// - /// The sender of this event - /// The current of the mouse cursor - private void MouseInfoProvider_OnMouseDown(object sender, System.Drawing.Point p) + private void MouseInfoProvider_OnPrimaryMouseDown(object sender, IntPtr wParam) { - ClipboardHelper.CopyToClipboard(ColorText); + HandleMouseClickAction(_userSettings.PrimaryClickAction.Value); + } - var color = GetColorString(); + private void MouseInfoProvider_OnMiddleMouseDown(object sender, IntPtr wParam) + { + HandleMouseClickAction(_userSettings.MiddleClickAction.Value); + } - var oldIndex = _userSettings.ColorHistory.IndexOf(color); + private void MouseInfoProvider_OnSecondaryMouseUp(object sender, IntPtr wParam) + { + HandleMouseClickAction(_userSettings.SecondaryClickAction.Value); + } + + private void HandleMouseClickAction(ColorPickerClickAction action) + { + switch (action) + { + case ColorPickerClickAction.PickColorThenEditor: + ClipboardHelper.CopyToClipboard(ColorText); + UpdateColorHistory(GetColorString()); + + _appStateHandler.OpenColorEditor(); + + break; + + case ColorPickerClickAction.PickColorAndClose: + ClipboardHelper.CopyToClipboard(ColorText); + UpdateColorHistory(GetColorString()); + + _appStateHandler.EndUserSession(); + + break; + + case ColorPickerClickAction.Close: + _appStateHandler.EndUserSession(); + break; + } + } + + private void UpdateColorHistory(string color) + { + int oldIndex = _userSettings.ColorHistory.IndexOf(color); if (oldIndex != -1) { _userSettings.ColorHistory.Move(oldIndex, 0); @@ -192,13 +226,6 @@ namespace ColorPicker.ViewModels { _userSettings.ColorHistory.RemoveAt(_userSettings.ColorHistory.Count - 1); } - - _appStateHandler.OnColorPickerMouseDown(); - } - - private void MouseInfoProvider_OnSecondaryMouseUp(object sender, IntPtr wParam) - { - _appStateHandler.EndUserSession(); } private string GetColorString() diff --git a/src/settings-ui/Settings.UI.Library/ColorPickerProperties.cs b/src/settings-ui/Settings.UI.Library/ColorPickerProperties.cs index b82ef56888..c4d870cfbd 100644 --- a/src/settings-ui/Settings.UI.Library/ColorPickerProperties.cs +++ b/src/settings-ui/Settings.UI.Library/ColorPickerProperties.cs @@ -40,7 +40,10 @@ namespace Microsoft.PowerToys.Settings.UI.Library VisibleColorFormats.Add("Decimal", new KeyValuePair(false, ColorFormatHelper.GetDefaultFormat("Decimal"))); VisibleColorFormats.Add("HEX Int", new KeyValuePair(false, ColorFormatHelper.GetDefaultFormat("HEX Int"))); ShowColorName = false; - ActivationAction = ColorPickerActivationAction.OpenColorPickerAndThenEditor; + ActivationAction = ColorPickerActivationAction.OpenColorPicker; + PrimaryClickAction = ColorPickerClickAction.PickColorThenEditor; + MiddleClickAction = ColorPickerClickAction.PickColorAndClose; + SecondaryClickAction = ColorPickerClickAction.Close; CopiedColorRepresentation = "HEX"; } @@ -57,6 +60,15 @@ namespace Microsoft.PowerToys.Settings.UI.Library [JsonPropertyName("activationaction")] public ColorPickerActivationAction ActivationAction { get; set; } + [JsonPropertyName("primaryclickaction")] + public ColorPickerClickAction PrimaryClickAction { get; set; } + + [JsonPropertyName("middleclickaction")] + public ColorPickerClickAction MiddleClickAction { get; set; } + + [JsonPropertyName("secondaryclickaction")] + public ColorPickerClickAction SecondaryClickAction { get; set; } + // Property ColorHistory is not used, the color history is saved separately in the colorHistory.json file [JsonPropertyName("colorhistory")] [CmdConfigureIgnoreAttribute] diff --git a/src/settings-ui/Settings.UI.Library/ColorPickerPropertiesVersion1.cs b/src/settings-ui/Settings.UI.Library/ColorPickerPropertiesVersion1.cs index d0e60d0eec..24f593213b 100644 --- a/src/settings-ui/Settings.UI.Library/ColorPickerPropertiesVersion1.cs +++ b/src/settings-ui/Settings.UI.Library/ColorPickerPropertiesVersion1.cs @@ -25,7 +25,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library VisibleColorFormats.Add("RGB", true); VisibleColorFormats.Add("HSL", true); ShowColorName = false; - ActivationAction = ColorPickerActivationAction.OpenColorPickerAndThenEditor; + ActivationAction = ColorPickerActivationAction.OpenColorPicker; } public HotkeySettings ActivationShortcut { get; set; } diff --git a/src/settings-ui/Settings.UI.Library/ColorPickerSettings.cs b/src/settings-ui/Settings.UI.Library/ColorPickerSettings.cs index 58a2b3f69d..641625e180 100644 --- a/src/settings-ui/Settings.UI.Library/ColorPickerSettings.cs +++ b/src/settings-ui/Settings.UI.Library/ColorPickerSettings.cs @@ -9,6 +9,7 @@ using System.Text.Json; using System.Text.Json.Serialization; using ManagedCommon; +using Microsoft.PowerToys.Settings.UI.Library.Enumerations; using Microsoft.PowerToys.Settings.UI.Library.Interfaces; namespace Microsoft.PowerToys.Settings.UI.Library @@ -23,7 +24,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library public ColorPickerSettings() { Properties = new ColorPickerProperties(); - Version = "2"; + Version = "2.1"; Name = ModuleName; } @@ -47,7 +48,21 @@ namespace Microsoft.PowerToys.Settings.UI.Library // This can be utilized in the future if the settings.json file is to be modified/deleted. public bool UpgradeSettingsConfiguration() - => false; + { + // Upgrading V1 to V2 doesn't set the version to 2.0, therefore V2 settings still report Version == 1.0 + if (Version == "1.0") + { + if (!Enum.IsDefined(Properties.ActivationAction)) + { + Properties.ActivationAction = ColorPickerActivationAction.OpenColorPicker; + } + + Version = "2.1"; + return true; + } + + return false; + } public static object UpgradeSettings(object oldSettingsObject) { diff --git a/src/settings-ui/Settings.UI.Library/Enumerations/ColorPickerActivationAction.cs b/src/settings-ui/Settings.UI.Library/Enumerations/ColorPickerActivationAction.cs index 459f6ddd2b..b3a9f55812 100644 --- a/src/settings-ui/Settings.UI.Library/Enumerations/ColorPickerActivationAction.cs +++ b/src/settings-ui/Settings.UI.Library/Enumerations/ColorPickerActivationAction.cs @@ -9,10 +9,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library.Enumerations // Activation shortcut opens editor OpenEditor, - // Activation shortcut opens color picker and after picking a color is copied into clipboard and opens editor - OpenColorPickerAndThenEditor, - - // Activation shortcut opens color picker only and picking color copies color into clipboard - OpenOnlyColorPicker, + // Activation shortcut opens color picker and after picking a color is copied into clipboard and editor optionally opens depending on which mouse button was pressed + OpenColorPicker, } } diff --git a/src/settings-ui/Settings.UI.Library/Enumerations/ColorPickerClickAction.cs b/src/settings-ui/Settings.UI.Library/Enumerations/ColorPickerClickAction.cs new file mode 100644 index 0000000000..9cb159f10f --- /dev/null +++ b/src/settings-ui/Settings.UI.Library/Enumerations/ColorPickerClickAction.cs @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.PowerToys.Settings.UI.Library.Enumerations +{ + public enum ColorPickerClickAction + { + // Clicking copies the picked color and opens the editor + PickColorThenEditor, + + // Clicking only copies the picked color and then exits color picker + PickColorAndClose, + + // Clicking exits color picker, without copying anything + Close, + } +} diff --git a/src/settings-ui/Settings.UI.Library/SettingsUtils.cs b/src/settings-ui/Settings.UI.Library/SettingsUtils.cs index 2c41850201..ea466f7077 100644 --- a/src/settings-ui/Settings.UI.Library/SettingsUtils.cs +++ b/src/settings-ui/Settings.UI.Library/SettingsUtils.cs @@ -130,6 +130,13 @@ namespace Microsoft.PowerToys.Settings.UI.Library T2 oldSettings = GetSettings(powertoy, fileName); T newSettings = (T)settingsUpgrader(oldSettings); Logger.LogInfo($"Settings file {fileName} for {powertoy} was read successfully in the old format."); + + // If the file needs to be modified, to save the new configurations accordingly. + if (newSettings.UpgradeSettingsConfiguration()) + { + SaveSettings(newSettings.ToJsonString(), powertoy, fileName); + } + return newSettings; } catch (Exception) diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/ColorPickerPage.xaml b/src/settings-ui/Settings.UI/SettingsXAML/Views/ColorPickerPage.xaml index 4d036a517a..3d95a5c724 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/ColorPickerPage.xaml +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/ColorPickerPage.xaml @@ -50,12 +50,39 @@ - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw index f4d91465d0..c1b7ec561d 100644 --- a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw +++ b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw @@ -1507,18 +1507,39 @@ Made with 💗 by Microsoft and the PowerToys community. Default color format - - Pick a color and open editor - - - Open editor - - - Only pick a color - Activation behavior + + Open editor + + + Pick a color first + + + Mouse actions + + + Choose what clicking individual buttons does + + + Primary click + + + Middle click + + + Secondary click + + + Pick a color and open editor + + + Pick a color and close + + + Close + Picker behavior diff --git a/src/settings-ui/Settings.UI/ViewModels/ColorPickerViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/ColorPickerViewModel.cs index d6006a58f7..5e7a9e85a4 100644 --- a/src/settings-ui/Settings.UI/ViewModels/ColorPickerViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/ColorPickerViewModel.cs @@ -182,6 +182,51 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels } } + public int PrimaryClickBehavior + { + get => (int)_colorPickerSettings.Properties.PrimaryClickAction; + + set + { + if (value != (int)_colorPickerSettings.Properties.PrimaryClickAction) + { + _colorPickerSettings.Properties.PrimaryClickAction = (ColorPickerClickAction)value; + OnPropertyChanged(nameof(PrimaryClickBehavior)); + NotifySettingsChanged(); + } + } + } + + public int MiddleClickBehavior + { + get => (int)_colorPickerSettings.Properties.MiddleClickAction; + + set + { + if (value != (int)_colorPickerSettings.Properties.MiddleClickAction) + { + _colorPickerSettings.Properties.MiddleClickAction = (ColorPickerClickAction)value; + OnPropertyChanged(nameof(MiddleClickBehavior)); + NotifySettingsChanged(); + } + } + } + + public int SecondaryClickBehavior + { + get => (int)_colorPickerSettings.Properties.SecondaryClickAction; + + set + { + if (value != (int)_colorPickerSettings.Properties.SecondaryClickAction) + { + _colorPickerSettings.Properties.SecondaryClickAction = (ColorPickerClickAction)value; + OnPropertyChanged(nameof(SecondaryClickBehavior)); + NotifySettingsChanged(); + } + } + } + public bool ShowColorName { get => _colorPickerSettings.Properties.ShowColorName; From abc5c3e24967fab10509032b094654c1706d8756 Mon Sep 17 00:00:00 2001 From: Heiko <61519853+htcfreek@users.noreply.github.com> Date: Fri, 13 Jun 2025 12:08:01 +0200 Subject: [PATCH 072/117] [RegistryPreview] Extended preview for value data (#37689) ## Summary of the Pull Request Add a button to the data grid that shows a windows for complex value preview. ### Screenshots **Button** ![image](https://github.com/user-attachments/assets/bb275831-be6d-490c-9193-5df719ce6c39) **Context menu** (Usefull on long data, if button is scrolled out of view.) ![image](https://github.com/user-attachments/assets/f5fb07ef-6f73-4eac-a289-9dce1c610ceb) **Preview: REG_SZ** ![image](https://github.com/user-attachments/assets/e03fbbc7-adaa-40d0-967c-7783b1a97b74) **Preview: REG_MULTI_SZ** ![image](https://github.com/user-attachments/assets/717590a6-7d91-4c9c-8e94-d875a5d2ba6b) **Preview: REG_EXPAND_SZ** ![image](https://github.com/user-attachments/assets/20135b66-528f-40e7-beed-adfc2b50313d) **Preview: REG_DWORD and REG_QWORD** ![image](https://github.com/user-attachments/assets/b60110ab-bfc7-40e7-ada3-d278a62b9d01) **Preview: REG_BINARY** ![image](https://github.com/user-attachments/assets/95f81036-6833-439e-8c01-b3a45c2d8edd) ![image](https://github.com/user-attachments/assets/ce237664-da96-4dbd-835f-969982560b9f) ## PR Checklist - [x] **Closes:** #36877 - [x] **Communication:** I've discussed this with core contributors already. If work hasn't been agreed, this work might be rejected - [x] **Tests:** Added/updated and all pass - [x] **Localization:** All end user facing strings can be localized => missing yet - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx ## Detailed Description of the Pull Request / Additional comments Additionally this PR updates the context menu for values. ## Validation Steps Performed Tested code with a local build. --- .github/actions/spell-check/allow/names.txt | 3 + .github/actions/spell-check/excludes.txt | 1 + .github/actions/spell-check/expect.txt | 1 + .pipelines/applyXamlStyling.ps1 | 7 +- Directory.Packages.props | 2 + NOTICE.md | 33 + src/codeAnalysis/GlobalSuppressions.cs | 44 + .../RegistryPreview/RegistryPreview.csproj | 2 +- .../RegistryPreviewXAML/App.xaml | 26 +- .../Controls/HexBox/AddressFormat.cs | 43 + .../Controls/HexBox/CanvasCommands.cs | 43 + .../Controls/HexBox/DataFormat.cs | 28 + .../Controls/HexBox/DataSignedness.cs | 28 + .../Controls/HexBox/DataType.cs | 43 + .../Controls/HexBox/HexBox.cs | 2913 +++++++++++++++++ .../EndianConvert/EndianBinaryReader.cs | 231 ++ .../EndianConvert/EndianBitConverter.cs | 186 ++ .../Library/EndianConvert/Endianness.cs | 28 + .../EndianConvert/FileFormatException.cs | 60 + .../Controls/HexBox/TextFormat.cs | 23 + .../Controls/HexBox/Themes/Generic.xaml | 170 + .../Controls/HexBox/TypeConverters.cs | 264 ++ .../Controls/HexBox/Utilities.cs | 81 + .../MonacoEditor}/MonacoEditorControl.xaml | 0 .../MonacoEditor}/MonacoEditorControl.xaml.cs | 0 .../MonacoEditor}/MonacoHelper.cs | 0 .../RegistryPreviewMainPage.DataPreview.cs | 341 ++ .../RegistryPreviewMainPage.Events.cs | 44 +- .../RegistryPreviewMainPage.Utilities.cs | 1 + .../RegistryPreviewMainPage.xaml | 59 +- .../RegistryPreviewUILib.csproj | 20 +- .../RegistryValue.xaml.cs | 7 +- .../Strings/en-US/Resources.resw | 69 +- .../RegistryPreviewUILib/Themes/Generic.xaml | 10 + 34 files changed, 4789 insertions(+), 22 deletions(-) create mode 100644 src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/AddressFormat.cs create mode 100644 src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/CanvasCommands.cs create mode 100644 src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/DataFormat.cs create mode 100644 src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/DataSignedness.cs create mode 100644 src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/DataType.cs create mode 100644 src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/HexBox.cs create mode 100644 src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/Library/EndianConvert/EndianBinaryReader.cs create mode 100644 src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/Library/EndianConvert/EndianBitConverter.cs create mode 100644 src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/Library/EndianConvert/Endianness.cs create mode 100644 src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/Library/EndianConvert/FileFormatException.cs create mode 100644 src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/TextFormat.cs create mode 100644 src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/Themes/Generic.xaml create mode 100644 src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/TypeConverters.cs create mode 100644 src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/Utilities.cs rename src/modules/registrypreview/RegistryPreviewUILib/{ => Controls/MonacoEditor}/MonacoEditorControl.xaml (100%) rename src/modules/registrypreview/RegistryPreviewUILib/{ => Controls/MonacoEditor}/MonacoEditorControl.xaml.cs (100%) rename src/modules/registrypreview/RegistryPreviewUILib/{ => Controls/MonacoEditor}/MonacoHelper.cs (100%) create mode 100644 src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.DataPreview.cs create mode 100644 src/modules/registrypreview/RegistryPreviewUILib/Themes/Generic.xaml diff --git a/.github/actions/spell-check/allow/names.txt b/.github/actions/spell-check/allow/names.txt index f5d259c0f2..f570a231af 100644 --- a/.github/actions/spell-check/allow/names.txt +++ b/.github/actions/spell-check/allow/names.txt @@ -91,6 +91,7 @@ Hemmerlein hlaueriksson Horvalds Howett +hotkidfamily htcfreek Huynh Ionut @@ -98,6 +99,7 @@ jamrobot Jaswal Jaylyn jefflord +Jeremic Jordi jyuwono kai @@ -222,6 +224,7 @@ openai Quickime regedit roslyn +Skia Spotify Vanara wangyi diff --git a/.github/actions/spell-check/excludes.txt b/.github/actions/spell-check/excludes.txt index f7fb1ec196..f5710e72ce 100644 --- a/.github/actions/spell-check/excludes.txt +++ b/.github/actions/spell-check/excludes.txt @@ -126,3 +126,4 @@ ^src/common/sysinternals/Eula/ ^tools/Verification scripts/Check preview handler registration\.ps1$ ignore$ +^src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/.*$ diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 1dcfdef0e1..68555a02f9 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -1513,6 +1513,7 @@ SICHINT SIDs siex sigdn +Signedness SIGNINGSCENARIO signtool SINGLEKEY diff --git a/.pipelines/applyXamlStyling.ps1 b/.pipelines/applyXamlStyling.ps1 index 7cb7b4a4b0..1facedc569 100644 --- a/.pipelines/applyXamlStyling.ps1 +++ b/.pipelines/applyXamlStyling.ps1 @@ -41,6 +41,9 @@ Write-Output "" Write-Output "Restoring dotnet tools..." dotnet tool restore --disable-parallel --no-cache +# Use Regex syntax +$PathExcludes = "(\\obj\\)|(\\bin\\)|(\\x64\\)|(\\Generated Files\\PowerRenameXAML\\)|(\\RegistryPreviewUILib\\Controls\\HexBox\\)" + if (-not $Passive) { # Look for unstaged changed files by default @@ -87,7 +90,7 @@ if (-not $Passive) } Write-Output "Running Git Diff: $gitDiffCommand" - $files = Invoke-Expression $gitDiffCommand | Select-String -Pattern "\.xaml$" + $files = Invoke-Expression $gitDiffCommand | Select-String -Pattern "\.xaml$" | Where-Object { $_ -notmatch $PathExcludes } if (-not $Passive -and -not $Main -and -not $Unstaged -and -not $Staged -and -not $LastCommit) { @@ -107,7 +110,7 @@ if (-not $Passive) else { Write-Output "Checking all files (passively)" - $files = Get-ChildItem -Path "$PSScriptRoot\..\src\*.xaml" -Recurse | Select-Object -ExpandProperty FullName | Where-Object { $_ -notmatch "(\\obj\\)|(\\bin\\)|(\\x64\\)|(\\Generated Files\\PowerRenameXAML\\)" } + $files = Get-ChildItem -Path "$PSScriptRoot\..\src\*.xaml" -Recurse | Select-Object -ExpandProperty FullName | Where-Object { $_ -notmatch $PathExcludes } if ($files.count -gt 0) { diff --git a/Directory.Packages.props b/Directory.Packages.props index 8116fe99b6..06f8e8ddef 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -69,6 +69,8 @@ + + diff --git a/NOTICE.md b/NOTICE.md index 2b94d67a4b..f36311d4ea 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -1427,6 +1427,37 @@ EXHIBIT A -Mozilla Public License. ## Utility: Registry Preview +### HexBox.WinUI + +We use HexBox.WinUI to show a preview of binary values. + +**Source**: https://github.com/hotkidfamily/HexBox.WinUI + +``` +MIT License + +Copyright (c) 2019 Filip Jeremic +Copyright (c) 2024~2025 hotkidfamily@gmail.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +``` + ### Monaco Editor **Source**: https://github.com/Microsoft/monaco-editor @@ -1457,6 +1488,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ``` + ## NuGet Packages used by PowerToys - AdaptiveCards.ObjectModel.WinUI3 2.0.0-beta @@ -1517,6 +1549,7 @@ SOFTWARE. - ReverseMarkdown 4.1.0 - ScipBe.Common.Office.OneNote 3.0.1 - SharpCompress 0.37.2 +- SkiaSharp.Views.WinUI 2.88.9 - StreamJsonRpc 2.21.69 - StyleCop.Analyzers 1.2.0-beta.556 - System.CodeDom 9.0.6 diff --git a/src/codeAnalysis/GlobalSuppressions.cs b/src/codeAnalysis/GlobalSuppressions.cs index d5ff98e548..ae544b0c76 100644 --- a/src/codeAnalysis/GlobalSuppressions.cs +++ b/src/codeAnalysis/GlobalSuppressions.cs @@ -76,3 +76,47 @@ using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "MVVMTK0049:Using [INotifyPropertyChanged] is not AOT compatible for WinRT", Justification = "Updated MVVM toolkit package introduced this.", Scope = "namespaceanddescendants", Target = "Peek.FilePreviewer")] [assembly: SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator", "MVVMTK0049:Using [INotifyPropertyChanged] is not AOT compatible for WinRT", Justification = "Updated MVVM toolkit package introduced this.", Scope = "type", Target = "~T:Peek.UI.Views.TitleBar")] [assembly: SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "MVVMTK0049:Using [INotifyPropertyChanged] is not AOT compatible for WinRT", Justification = "Updated MVVM toolkit package introduced this.", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib")] + +// HexBox control in RegistryPreviewUILib (We decided to copy the original code and not fix all theses problems for easier updating.) +[assembly: SuppressMessage("Design", "CA1001:Types that own disposable fields should be disposable", Justification = "", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")] +[assembly: SuppressMessage("Design", "CA1051:Do not declare visible instance fields", Justification = "", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")] +[assembly: SuppressMessage("Globalization", "CA1305:Specify IFormatProvider", Justification = "", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")] +[assembly: SuppressMessage("Naming", "CA1720:Identifiers should not contain type names", Justification = "", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")] +[assembly: SuppressMessage("Performance", "CA1805:Do not initialize unnecessarily", Justification = "", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")] +[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1623:Property summary documentation should match accessors", Justification = "", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")] +[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1642:Constructor summary documentation should begin with standard text", Justification = "", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")] +[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1648: has been used on an element that doesn't inherit from a base class or implement an interface.", Justification = "", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")] +[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1649:File name should match first type name", Justification = "", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")] +[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1500:Braces for multi-line statements should not share line", Justification = "", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")] +[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1502:Element should not be on a single line", Justification = "", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")] +[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1503:Braces should not be omitted", Justification = "", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")] +[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1505:Opening braces should not be followed by blank line", Justification = "", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")] +[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1507:Code should not contain multiple blank lines in a row", Justification = "", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")] +[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1508:Closing braces should not be preceded by blank line", Justification = "", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")] +[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1509:Opening braces should not be preceded by blank line", Justification = "", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")] +[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1512:Single-line comments should not be followed by blank line", Justification = "", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")] +[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1513:Closing brace should be followed by blank line", Justification = "", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")] +[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1514:Element documentation header should be preceded by blank line", Justification = "", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")] +[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1515:Single-line comment should be preceded by blank line", Justification = "", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")] +[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1516:Elements should be separated by blank line", Justification = "", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")] +[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1119:Statement should not use unnecessary parenthesis", Justification = "", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")] +[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")] +[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")] +[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1407:Arithmetic expressions should declare precedence", Justification = "", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")] +[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1413:Use trailing comma in multi-line initializers", Justification = "", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")] +[assembly: SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")] +[assembly: SuppressMessage("StyleCop.CSharp.NamingRules", "SA1306:Field names should begin with lower-case letter", Justification = "", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")] +[assembly: SuppressMessage("StyleCop.CSharp.NamingRules", "SA1312:Variable names should begin with lower-case letter", Justification = "", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")] +[assembly: SuppressMessage("StyleCop.CSharp.NamingRules", "SA1313:Parameter names should begin with lower-case letter", Justification = "", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1200:Using directives should be placed correctly", Justification = "", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")] +[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1108:Block statements should not contain embedded comments", Justification = "", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")] +[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1116:Split parameters should start on line after declaration", Justification = "", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")] +[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1117:Parameters should be on same line or separate lines", Justification = "", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")] +[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1129:Do not use default value type constructor", Justification = "", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")] +[assembly: SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1000:Keywords should be spaced correctly", Justification = "", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")] +[assembly: SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1003:Symbols should be spaced correctly", Justification = "", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")] +[assembly: SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1005:Single line comments should begin with single space", Justification = "", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")] +[assembly: SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1024:Colons Should Be Spaced Correctly", Justification = "", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")] +[assembly: SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1025:Code should not contain multiple whitespace in a row", Justification = "", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")] +[assembly: SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1028:Code should not contain trailing whitespace", Justification = "", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")] +[assembly: SuppressMessage("Usage", "CsWinRT1028:Class is not marked partial", Justification = "", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")] diff --git a/src/modules/registrypreview/RegistryPreview/RegistryPreview.csproj b/src/modules/registrypreview/RegistryPreview/RegistryPreview.csproj index 36bd1f4f4d..8ca723808f 100644 --- a/src/modules/registrypreview/RegistryPreview/RegistryPreview.csproj +++ b/src/modules/registrypreview/RegistryPreview/RegistryPreview.csproj @@ -31,7 +31,7 @@ - + diff --git a/src/modules/registrypreview/RegistryPreview/RegistryPreviewXAML/App.xaml b/src/modules/registrypreview/RegistryPreview/RegistryPreviewXAML/App.xaml index 7c2836890c..400d0c71fd 100644 --- a/src/modules/registrypreview/RegistryPreview/RegistryPreviewXAML/App.xaml +++ b/src/modules/registrypreview/RegistryPreview/RegistryPreviewXAML/App.xaml @@ -10,7 +10,31 @@ - + + + + + + + + + 1 + 1,1,1,2 + + + + + + + + + + + + 1 + 2 + + diff --git a/src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/AddressFormat.cs b/src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/AddressFormat.cs new file mode 100644 index 0000000000..8219ed8b51 --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/AddressFormat.cs @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// +// 2020-... created by Filip Jeremic (fjeremic) as "HexView.Wpf". +// 2024-... republished by @hotkidfamily as "HexBox.WinUI". +// 2025 Included in PowerToys. (Branch master; commit 72dcf64dc858c693a7a16887004c8ddbab61fce7.) +// + +namespace RegistryPreviewUILib.HexBox +{ + /// + /// Enumerates the address column formatting options. + /// + public enum AddressFormat + { + /// + /// 16 bit HEX address "0000". + /// + Address16, + + /// + /// 24 bit HEX address "00:0000". + /// + Address24, + + /// + /// 32 bit HEX address "0000:0000". + /// + Address32, + + /// + /// 48 bit HEX address "0000:00000000". + /// + Address48, + + /// + /// 64 bit HEX address "00000000:00000000". + /// + Address64, + } +} diff --git a/src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/CanvasCommands.cs b/src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/CanvasCommands.cs new file mode 100644 index 0000000000..dca1f95725 --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/CanvasCommands.cs @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// +// 2020-... created by Filip Jeremic (fjeremic) as "HexView.Wpf". +// 2024-... republished by @hotkidfamily as "HexBox.WinUI". +// 2025 Included in PowerToys. (Branch master; commit 72dcf64dc858c693a7a16887004c8ddbab61fce7.) +// +using System; +using System.Windows.Input; + +namespace RegistryPreviewUILib.HexBox +{ + public class RelayCommand : ICommand + { + private readonly Action _execute; + private readonly Func _canExecute; + + public RelayCommand(Action execute, Func canExecute = null) + { + _execute = execute; + _canExecute = canExecute; + } + + public event EventHandler CanExecuteChanged; + + public bool CanExecute(object parameter) + { + return _canExecute == null || _canExecute(parameter); + } + + public void Execute(object parameter) + { + _execute(parameter); + } + + public void RaiseCanExecuteChanged() + { + CanExecuteChanged?.Invoke(this, EventArgs.Empty); + } + } +} diff --git a/src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/DataFormat.cs b/src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/DataFormat.cs new file mode 100644 index 0000000000..31929f8b88 --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/DataFormat.cs @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// +// 2020-... created by Filip Jeremic (fjeremic) as "HexView.Wpf". +// 2024-... republished by @hotkidfamily as "HexBox.WinUI". +// 2025 Included in PowerToys. (Branch master; commit 72dcf64dc858c693a7a16887004c8ddbab61fce7.) +// + +namespace RegistryPreviewUILib.HexBox +{ + /// + /// Enumerates the format to display integral data in. + /// + public enum DataFormat + { + /// + /// Display the data in decimal format. + /// + Decimal, + + /// + /// Display the data in hexadecimal format. + /// + Hexadecimal, + } +} diff --git a/src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/DataSignedness.cs b/src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/DataSignedness.cs new file mode 100644 index 0000000000..4f5d95bc93 --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/DataSignedness.cs @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// +// 2020-... created by Filip Jeremic (fjeremic) as "HexView.Wpf". +// 2024-... republished by @hotkidfamily as "HexBox.WinUI". +// 2025 Included in PowerToys. (Branch master; commit 72dcf64dc858c693a7a16887004c8ddbab61fce7.) +// + +namespace RegistryPreviewUILib.HexBox +{ + /// + /// Enumerates the signedness of the data to display. + /// + public enum DataSignedness + { + /// + /// Display the data as signed values. + /// + Signed, + + /// + /// Display the data as unsigned values. + /// + Unsigned, + } +} diff --git a/src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/DataType.cs b/src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/DataType.cs new file mode 100644 index 0000000000..c9619bfc6b --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/DataType.cs @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// +// 2020-... created by Filip Jeremic (fjeremic) as "HexView.Wpf". +// 2024-... republished by @hotkidfamily as "HexBox.WinUI". +// 2025 Included in PowerToys. (Branch master; commit 72dcf64dc858c693a7a16887004c8ddbab61fce7.) +// + +namespace RegistryPreviewUILib.HexBox +{ + /// + /// Enumerates how the data (bytes read from the buffer) is to be interpreted when displayed. + /// + public enum DataType + { + /// + /// Display the data as integral (integer) values. + /// + Int_1 = 1, + /// + /// Display the data as integral (integer) values. + /// + Int_2 = 2, + /// + /// Display the data as integral (integer) values. + /// + Int_4 = 4, + /// + /// Display the data as integral (integer) values. + /// + Int_8 = 8, + /// + /// Display the data as floating point values. + /// + Float_32 = 32, + /// + /// Display the data as floating point values. + /// + Float_64 = 64, + } +} diff --git a/src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/HexBox.cs b/src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/HexBox.cs new file mode 100644 index 0000000000..46df5a60ba --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/HexBox.cs @@ -0,0 +1,2913 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// +// 2020-... created by Filip Jeremic (fjeremic) as "HexView.Wpf". +// 2024-... republished by @hotkidfamily as "HexBox.WinUI". +// 2025 Included in PowerToys. (Branch master; commit 72dcf64dc858c693a7a16887004c8ddbab61fce7.) +// +#pragma warning disable SA1210 // Using directives should be ordered alphabetically by namespace +#pragma warning disable SA1208 // System using directives should be placed before other using directives +using RegistryPreviewUILib.HexBox.Library.EndianConvert; +using Microsoft.UI; +using Microsoft.UI.Input; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Controls.Primitives; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media; +using SkiaSharp; +using SkiaSharp.Views.Windows; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Runtime.CompilerServices; +using System.Text; +using System.Windows.Input; +using Windows.ApplicationModel.DataTransfer; +using Windows.Foundation; +using Windows.System; +using Windows.UI.Core; +#pragma warning restore SA1208 // System using directives should be placed before other using directives +#pragma warning restore SA1210 // Using directives should be ordered alphabetically by namespace + +// To learn more about WinUI, the WinUI project structure, +// and more about our project templates, see: http://aka.ms/winui-project-info. + +namespace RegistryPreviewUILib.HexBox +{ + [TemplatePart(Name = "ElementCanvas", Type = typeof(SKXamlCanvas))] + [TemplatePart(Name = "ElementScrollBar", Type = typeof(ScrollBar))] + public sealed class HexBox : Control, INotifyPropertyChanged + { + /// + /// Defines the address at which the data in the begins. + /// + public static readonly DependencyProperty AddressProperty = + DependencyProperty.Register(nameof(Address), typeof(ulong), typeof(HexBox), + new PropertyMetadata(0UL, OnAddressChanged)); + + /// + /// Defines the brush used to display the addresses in the address section of the control. + /// + public static readonly DependencyProperty AddressBrushProperty = + DependencyProperty.Register(nameof(AddressBrush), typeof(SolidColorBrush), typeof(HexBox), + new PropertyMetadata(new SolidColorBrush(Colors.CornflowerBlue), OnPropertyChangedInvalidateVisual)); + + /// + /// Defines the width of the addresses displayed in the address section of the control. + /// + public static readonly DependencyProperty AddressFormatProperty = + DependencyProperty.Register(nameof(AddressFormat), typeof(AddressFormat), typeof(HexBox), + new PropertyMetadata(AddressFormat.Address32, OnPropertyChangedInvalidateVisual)); + + /// + /// Defines the brush used for alternating for text in alternating (odd numbered) columns in the data section of the control. + /// + public static readonly DependencyProperty AlternatingDataColumnTextBrushProperty = + DependencyProperty.Register(nameof(AlternatingDataColumnTextBrush), typeof(SolidColorBrush), typeof(HexBox), + new PropertyMetadata(new SolidColorBrush(Colors.Gray), OnPropertyChangedInvalidateVisual)); + + /// + /// Defines the number of columns to display. + /// + public static readonly DependencyProperty ColumnsProperty = + DependencyProperty.Register(nameof(Columns), typeof(int), typeof(HexBox), + new PropertyMetadata(16, OnPropertyChangedInvalidateVisual)); + + /// + /// Defines the endianness used to interpret the data. + /// + public static readonly DependencyProperty EndiannessProperty = + DependencyProperty.Register(nameof(Endianness), typeof(Endianness), typeof(HexBox), + new PropertyMetadata(Endianness.BigEndian, OnPropertyChangedInvalidateVisual)); + + /// + /// Defines the format of the data to display. + /// + public static readonly DependencyProperty DataFormatProperty = + DependencyProperty.Register(nameof(DataFormat), typeof(DataFormat), typeof(HexBox), + new PropertyMetadata(DataFormat.Hexadecimal, OnPropertyChangedInvalidateVisual)); + + /// + /// Defines the signedness of the data to display. + /// + public static readonly DependencyProperty DataSignednessProperty = + DependencyProperty.Register(nameof(DataSignedness), typeof(DataSignedness), typeof(HexBox), + new PropertyMetadata(DataSignedness.Unsigned, OnPropertyChangedInvalidateVisual)); + + /// + /// Defines the data source which is used to read the data to display within this control. + /// + public static readonly DependencyProperty DataSourceProperty = + DependencyProperty.Register(nameof(DataSource), typeof(BinaryReader), typeof(HexBox), + new PropertyMetadata(null, OnDataSourceChanged)); + + /// + /// Defines the offset from the of the first visible data element being displayed. + /// + public static readonly DependencyProperty OffsetProperty = + DependencyProperty.Register(nameof(Offset), typeof(long), typeof(HexBox), + new PropertyMetadata(0L, OnPropertyChangedInvalidateVisual)); + + /// + /// Defines the maximum number of columns, based on the size of the control, which can be displayed. + /// + public static readonly DependencyProperty MaxVisibleColumnsProperty = + DependencyProperty.Register(nameof(MaxVisibleColumns), typeof(int), typeof(HexBox), + new PropertyMetadata(int.MaxValue, OnPropertyChangedInvalidateVisual)); + + + /// + /// Defines the maximum number of rows, based on the size of the control, which can be displayed. + /// + public static readonly DependencyProperty MaxVisibleRowsProperty = + DependencyProperty.Register(nameof(MaxVisibleRows), typeof(int), typeof(HexBox), + new PropertyMetadata(int.MaxValue, OnPropertyChangedInvalidateVisual)); + + /// + /// Defines the brush used for selection fill. + /// + public static readonly DependencyProperty SelectionBrushProperty = + DependencyProperty.Register(nameof(SelectionBrush), typeof(SolidColorBrush), typeof(HexBox), + new PropertyMetadata(new SolidColorBrush(Colors.LightPink), OnPropertyChangedInvalidateVisual)); + + /// + /// Defines the brush used for selected text. + /// + public static readonly DependencyProperty SelectionTextBrushProperty = + DependencyProperty.Register(nameof(SelectionTextBrush), typeof(SolidColorBrush), typeof(HexBox), + new PropertyMetadata(new SolidColorBrush(Colors.Black), OnPropertyChangedInvalidateVisual)); + + /// + /// Defines the offset from of where the user selection has ended. + /// + public static readonly DependencyProperty SelectionEndProperty = + DependencyProperty.Register(nameof(SelectionEnd), typeof(long), typeof(HexBox), + new PropertyMetadata(0L, OnSelectionEndChanged)); + + /// + /// Defines the offset from of where the user selection has started. + /// + public static readonly DependencyProperty SelectionStartProperty = + DependencyProperty.Register(nameof(SelectionStart), typeof(long), typeof(HexBox), + new PropertyMetadata(0L, OnSelectionStartChanged)); + + /// + /// Determines whether the user can change the layout and data format. + /// + public static readonly DependencyProperty EnforcePropertiesProperty = + DependencyProperty.Register(nameof(EnforceProperties), typeof(bool), typeof(HexBox), + new PropertyMetadata(false, OnPropertyChangedInvalidateVisual)); + + /// + /// Determines whether to show the address section of the control. + /// + public static readonly DependencyProperty ShowAddressProperty = + DependencyProperty.Register(nameof(ShowAddress), typeof(bool), typeof(HexBox), + new PropertyMetadata(true, OnPropertyChangedInvalidateVisual)); + + /// + /// Determines whether to show the data section of the control. + /// + public static readonly DependencyProperty ShowDataProperty = + DependencyProperty.Register(nameof(ShowData), typeof(bool), typeof(HexBox), + new PropertyMetadata(true, OnPropertyChangedInvalidateVisual)); + + /// + /// Determines whether to show the text section of the control. + /// + public static readonly DependencyProperty ShowTextProperty = + DependencyProperty.Register(nameof(ShowText), typeof(bool), typeof(HexBox), + new PropertyMetadata(true, OnPropertyChangedInvalidateVisual)); + + /// + /// Defines the brush used for the fill of the vertical separator line between the areas. + /// + public static readonly DependencyProperty VerticalSeparatorLineBrushProperty = + DependencyProperty.Register(nameof(VerticalSeparatorLineBrush), typeof(SolidColorBrush), typeof(HexBox), + new PropertyMetadata(new SolidColorBrush(Colors.Black), OnPropertyChangedInvalidateVisual)); + + + /// + /// Defines the format of the text to display in the text section. + /// + public static readonly DependencyProperty TextFormatProperty = + DependencyProperty.Register(nameof(TextFormat), typeof(TextFormat), typeof(HexBox), + new PropertyMetadata(TextFormat.Ascii, OnPropertyChangedInvalidateVisual)); + + /// + /// Gets the command. + /// + public ICommand SelectAllCommand + { + get { return (ICommand)GetValue(SelectAllCommandProperty); } + set { SetValue(SelectAllCommandProperty, value); } + } + + // Using a DependencyProperty as the backing store for CopyCommand. This enables animation, styling, binding, etc... + public static readonly DependencyProperty SelectAllCommandProperty = + DependencyProperty.Register("SelectAllCommand", typeof(ICommand), typeof(HexBox), new PropertyMetadata(null)); + + /// + /// Gets the command. + /// + public ICommand CopyCommand + { + get { return (ICommand)GetValue(CopyCommandProperty); } + set { SetValue(CopyCommandProperty, value); } + } + + // Using a DependencyProperty as the backing store for CopyCommand. This enables animation, styling, binding, etc... + public static readonly DependencyProperty CopyCommandProperty = + DependencyProperty.Register("CopyCommand", typeof(ICommand), typeof(HexBox), new PropertyMetadata(null)); + + /// + /// Gets the for text command. + /// + public ICommand CopyTextCommand + { + get { return (ICommand)GetValue(CopyTextCommandProperty); } + set { SetValue(CopyTextCommandProperty, value); } + } + + // Using a DependencyProperty as the backing store for CopyTextCommand. This enables animation, styling, binding, etc... + public static readonly DependencyProperty CopyTextCommandProperty = + DependencyProperty.Register("CopyTextCommand", typeof(ICommand), typeof(HexBox), new PropertyMetadata(null)); + + + private const int _MaxColumns = 128; + private const int _MaxRows = 128; + + private const int _CharsBetweenSections = 2; + private const int _CharsBetweenDataColumns = 1; + private const int _ScrollWheelScrollRows = 3; + + private Rect _AddressRect; + private Rect _DataRect; + private Rect _TextRect; + + private SKPaint _TextPaint; + private SKPaint _LinePaint; + private SKRect _TextMeasure; + private SKTypeface _TextTypeFace; + + private SKXamlCanvas _Canvas; + private string _CanvasName = "ElementCanvas"; + + private SelectionArea _HighlightBegin = SelectionArea.None; + private SelectionArea _HighlightState = SelectionArea.None; + + private double _LastVerticalScrollValue = 0; + + private ScrollBar _ScrollBar; + private string _ScrollBarName = "ElementScrollBar"; + + private SelectionAdjustment _pointerMoveSelectionAdjustment = SelectionAdjustment.None; + + /// + public event PropertyChangedEventHandler PropertyChanged; + + private enum SelectionArea + { + None, + Address, + Data, + Text, + } + + private enum SelectionAdjustment + { + None, + Up, + Down + } + + /// + /// Gets or sets the address at which the data in the begins. + /// + public ulong Address + { + get => (ulong)GetValue(AddressProperty); + + set => SetValue(AddressProperty, value); + } + + /// + /// Gets or sets the brush used to display the addresses in the address section of the control. + /// + public SolidColorBrush AddressBrush + { + get => (SolidColorBrush)GetValue(AddressBrushProperty); + + set => SetValue(AddressBrushProperty, value); + } + + /// + /// Gets or sets the brush used for alternating for text in alternating (odd numbered) columns in the data section of the control. + /// + public SolidColorBrush AlternatingDataColumnTextBrush + { + get => (SolidColorBrush)GetValue(AlternatingDataColumnTextBrushProperty); + + set => SetValue(AlternatingDataColumnTextBrushProperty, value); + } + + /// + /// Gets or sets the number of columns to display. + /// + public int Columns + { + get => (int)GetValue(ColumnsProperty); + + set => SetValue(ColumnsProperty, CoerceColumns(this, value)); + } + + /// + /// Gets or sets the endianness used to interpret the data. + /// + public Endianness Endianness + { + get => (Endianness)GetValue(EndiannessProperty); + + set => SetValue(EndiannessProperty, value); + } + + /// + /// Gets or sets the format of the data to display. + /// + public DataFormat DataFormat + { + get => (DataFormat)GetValue(DataFormatProperty); + + set => SetValue(DataFormatProperty, value); + } + + /// + /// Gets or sets the signedness of the data to display. + /// + public DataSignedness DataSignedness + { + get => (DataSignedness)GetValue(DataSignednessProperty); + + set => SetValue(DataSignednessProperty, value); + } + + /// + /// Gets or sets the data source which is used to read the data to display within this control. + /// + public BinaryReader DataSource + { + get => (BinaryReader)GetValue(DataSourceProperty); + + set => SetValue(DataSourceProperty, value); + } + + /// + /// Gets or sets the data type which is used to display within this control. + /// + public DataType DataType + { + get { return (DataType)GetValue(DataTypeProperty); } + set => SetValue(DataTypeProperty, value); + } + + // Using a DependencyProperty as the backing store for DataType. This enables animation, styling, binding, etc... + public static readonly DependencyProperty DataTypeProperty = + DependencyProperty.Register("DataType", typeof(DataType), typeof(HexBox), new PropertyMetadata(DataType.Int_1, OnDataTypeChanged)); + + /// + /// Gets or sets the width of the data to display. + /// + private int DataWidth = 1; + + /// + /// Gets a value indicating whether the user has made any selection within the control. + /// + public bool IsSelectionActive => SelectionLength != 0; + + /// + /// Gets the maximum number of columns, based on the size of the control, which can be displayed. + /// + public int MaxVisibleColumns + { + get => (int)GetValue(MaxVisibleColumnsProperty); + + private set => SetValue(MaxVisibleColumnsProperty, CoerceMaxVisibleColumns(this, value)); + } + + /// + /// Gets the maximum number of rows, based on the size of the control, which can be displayed. + /// + public int MaxVisibleRows + { + get => (int)GetValue(MaxVisibleRowsProperty); + + private set => SetValue(MaxVisibleRowsProperty, CoerceMaxVisibleRows(this, value)); + } + + /// + /// Gets or sets the offset from the of the first visible data element being displayed. + /// + public long Offset + { + get => (long)GetValue(OffsetProperty); + + set => SetValue(OffsetProperty, CoerceOffset(this, value)); + } + + /// + /// Gets lowest order address currently being selected. + /// + public ulong SelectedAddress => Address + (ulong)SelectedOffset; + + /// + /// Gets the offset from of the . + /// + public long SelectedOffset => Math.Min(SelectionStart, SelectionEnd); + + /// + /// Gets or sets the brush used for selection fill. + /// + public SolidColorBrush SelectionBrush + { + get => (SolidColorBrush)GetValue(SelectionBrushProperty); + + set => SetValue(SelectionBrushProperty, value); + } + + /// + /// Gets the offset from of where the user selection has ended. + /// + public long SelectionEnd + { + get => (long)GetValue(SelectionEndProperty); + + private set => SetValue(SelectionEndProperty, CoerceSelectionEnd(this, value)); + } + + /// + /// Gets the number of bytes selected. + /// + public long SelectionLength + { + get + { + if (SelectionStart <= SelectionEnd) + { + return SelectionEnd - SelectionStart; + } + else + { + return SelectionStart - SelectionEnd + _BytesPerColumn; + } + } + } + + /// + /// Gets the offset from of where the user selection has started. + /// + public long SelectionStart + { + get => (long)GetValue(SelectionStartProperty); + + private set + { + SetValue(SelectionStartProperty, CoerceSelectionStart(this, value)); + + // Reset SelectionStart adjustment state + _pointerMoveSelectionAdjustment = SelectionAdjustment.None; + } + } + + /// + /// Gets or sets the brush used for selected text. + /// + public SolidColorBrush SelectionTextBrush + { + get => (SolidColorBrush)GetValue(SelectionTextBrushProperty); + + set => SetValue(SelectionTextBrushProperty, value); + } + + /// + /// Gets or sets a value indicating whether the user can change the layout and data format or not. + /// + public bool EnforceProperties + { + get => (bool)GetValue(EnforcePropertiesProperty); + set => SetValue(EnforcePropertiesProperty, value); + } + + /// + /// Gets or sets a value indicating whether to show the address section of the control. + /// + public bool ShowAddress + { + get => (bool)GetValue(ShowAddressProperty); + + set => SetValue(ShowAddressProperty, value); + } + + /// + /// Gets or sets a value indicating whether to show the data section of the control. + /// + public bool ShowData + { + get => (bool)GetValue(ShowDataProperty); + + set => SetValue(ShowDataProperty, value); + } + + /// + /// Gets or sets a value indicating whether to show the text section of the control. + /// + public bool ShowText + { + get => (bool)GetValue(ShowTextProperty); + + set => SetValue(ShowTextProperty, value); + } + + /// + /// Gets or sets the brush used to display the vertical separator line between the control areas. + /// + public SolidColorBrush VerticalSeparatorLineBrush + { + get => (SolidColorBrush)GetValue(VerticalSeparatorLineBrushProperty); + + set => SetValue(VerticalSeparatorLineBrushProperty, value); + } + + /// + /// Gets or sets the width of the addresses displayed in the address section of the control. + /// + public AddressFormat AddressFormat + { + get => (AddressFormat)GetValue(AddressFormatProperty); + + set => SetValue(AddressFormatProperty, value); + } + + /// + /// Gets or sets the format of the text to display in the text section. + /// + public TextFormat TextFormat + { + get => (TextFormat)GetValue(TextFormatProperty); + + set => SetValue(TextFormatProperty, value); + } + + private double _SelectionBoxDataXPadding => _TextMeasure.Width / 4; + + private double _SelectionBoxDataYPadding => 0; + + private double _SelectionBoxTextXPadding => 0; + + private double _SelectionBoxTextYPadding => 0; + + private int _BytesPerColumn => DataWidth; + + private int _BytesPerRow => DataWidth * Columns; + + public class HighlightedRegion + { + public long Start; + public long Length; + public long End { get { return Start + Length; } } + public Brush Color; + + public HighlightedRegion() + { + + } + + public HighlightedRegion(int Start, int Length, Brush Color) + { + this.Start = Start; + this.Length = Length; + this.Color = Color; + } + + public bool IsByteSelected(long BytePos) + { + return BytePos >= Start && BytePos <= End; + } + } + + public List HighlightedRegions + { + get { return (List)GetValue(HighlightedRegionsProperty); } + set { SetValue(HighlightedRegionsProperty, value); } + } + + // Using a DependencyProperty as the backing store for HighlightedRegions. This enables animation, styling, binding, etc... + public static readonly DependencyProperty HighlightedRegionsProperty = + DependencyProperty.Register("HighlightedRegions", typeof(List), typeof(HexBox), new PropertyMetadata(new List(), OnPropertyChangedInvalidateVisual)); + + + /// + /// Clears the current selection + /// + public void ClearSelection() + { + SelectionStart = SelectionEnd = 0; + } + + + /// + /// Select all data. + /// + public void SelectAll() + { + SelectionStart = 0; + SelectionEnd = DataSource.BaseStream.Length; + } + + + /// + /// Copies the current selection of the control to the . + /// + /// Copy the text and not the data. + public void Copy(bool copyText) + { + if (IsSelectionActive) + { + StringBuilder builder = new(); + + long savedDataSourcePositionBeforeReadingData = DataSource.BaseStream.Position; + + // Adjust wrong SelectionEnd after selecting down or left to right + long selectionEnd = SelectionStart < SelectionEnd ? SelectionEnd - _BytesPerColumn : SelectionEnd; + + DataSource.BaseStream.Position = Math.Min(SelectionStart, selectionEnd); + + while (DataSource.BaseStream.Position <= Math.Max(SelectionStart, selectionEnd)) + { + if (copyText) + { + var formattedData = ReadFormattedText(); + builder.Append(formattedData); + } + else + { + var formattedData = ReadFormattedData(); + builder.Append(formattedData); + } + } + + DataSource.BaseStream.Position = savedDataSourcePositionBeforeReadingData; + + var dataPackage = new DataPackage(); + dataPackage.SetText(builder.ToString()); + Clipboard.SetContent(dataPackage); + } + } + + protected override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + _Canvas = GetTemplateChild(_CanvasName) as SKXamlCanvas; + + if (_Canvas != null) + { + CopyCommand = new RelayCommand(CopyExecuted, CopyCanExecute); + CopyTextCommand = new RelayCommand(CopyTextExecuted, CopyCanExecute); + SelectAllCommand = new RelayCommand(SelectAllExecuted, SelectAllCanExecute); + _Canvas.PaintSurface += Canvas_PaintSurface; + } + else + { + throw new InvalidOperationException($"Could not find {_CanvasName} template child."); + } + + if (_ScrollBar != null) + { + _ScrollBar.Scroll -= OnVerticalScrollBarScroll; + } + + _ScrollBar = GetTemplateChild(_ScrollBarName) as ScrollBar; + + if (_ScrollBar != null) + { + _ScrollBar.Scroll += OnVerticalScrollBarScroll; + _ScrollBar.ValueChanged += OnVerticalScrollBarValueChanged; + + _ScrollBar.Minimum = 0; + _ScrollBar.SmallChange = 1; + _ScrollBar.LargeChange = MaxVisibleRows; + _TextTypeFace = SKTypeface.FromFamilyName(_ScrollBar.FontFamily.Source, SKFontStyle.Normal); + } + else + { + throw new InvalidOperationException($"Could not find {_ScrollBarName} template child."); + } + } + + private void DrawSelectionGeometry(SKCanvas Canvas, + Brush brush, + SKPaint pen, + Point point0, + Point point1, + SelectionArea relativeTo) + { + if ((long)point0.Y > (long)point1.Y) + { + throw new ArgumentException($"{point0.ToString()} > {point1.ToString()}", nameof(point0)); + } + + Point lhsVerticalLinePoint0; + Point rhsVerticalLinePoint0; + + double selectionBoxXPadding; + double selectionBoxYPadding; + + switch (relativeTo) + { + case SelectionArea.Data: + { + lhsVerticalLinePoint0 = new Point(_AddressRect.Left, _AddressRect.Top); + rhsVerticalLinePoint0 = new Point(_DataRect.Left, _DataRect.Top); + + selectionBoxXPadding = _SelectionBoxDataXPadding; + selectionBoxYPadding = _SelectionBoxDataYPadding; + } + + break; + + case SelectionArea.Text: + { + lhsVerticalLinePoint0 = new Point(_DataRect.Left, _DataRect.Top); + rhsVerticalLinePoint0 = new Point(_TextRect.Left, _TextRect.Top); + + selectionBoxXPadding = _SelectionBoxTextXPadding; + selectionBoxYPadding = _SelectionBoxTextYPadding; + } + + break; + + default: + { + throw new ArgumentException($"Invalid relative area {relativeTo}", nameof(relativeTo)); + } + } + + point0.X -= selectionBoxXPadding; + point1.X += selectionBoxXPadding; + point0.Y -= selectionBoxYPadding; + point1.Y += selectionBoxYPadding; + + var ps_CharsBetweenSections = _CharsBetweenSections * _TextMeasure.Width; + + SKPath path = new(); + SKPoint[] points; + + if ((long)point0.X < (long)point1.X) + { + if ((long)point0.Y < (long)point1.Y) + { + // +---------------------------+ + // | | + // | 0-------------2 + // | | | + // 6-------------7 1-------3 + // | | | + // 5-------------------4 | + // | | + // | | + // | | + // +---------------------------+ + Point point2 = new(rhsVerticalLinePoint0.X - ps_CharsBetweenSections + selectionBoxXPadding, point0.Y); + Point point3 = new(rhsVerticalLinePoint0.X - ps_CharsBetweenSections + selectionBoxXPadding, point1.Y); + Point point4 = new(point1.X, point1.Y + _TextMeasure.Height); + Point point5 = new(lhsVerticalLinePoint0.X + ps_CharsBetweenSections - selectionBoxXPadding, point1.Y + _TextMeasure.Height); + Point point6 = new(lhsVerticalLinePoint0.X + ps_CharsBetweenSections - selectionBoxXPadding, point0.Y + _TextMeasure.Height); + Point point7 = new(point0.X, point0.Y + _TextMeasure.Height); + + points = [point0.ToSKPoint(), point2.ToSKPoint(), point3.ToSKPoint(), point1.ToSKPoint(), point4.ToSKPoint(), point5.ToSKPoint(), point6.ToSKPoint(), point7.ToSKPoint()]; + } + else + { + // +---------------------------+ + // | | + // | 0-------------1 | + // | | | | + // | 3-------------2 | + // | | + // | | + // | | + // | | + // | | + // +---------------------------+ + Point point2 = new(point1.X, point1.Y + _TextMeasure.Height); + Point point3 = new(point0.X, point0.Y + _TextMeasure.Height); + + points = [point0.ToSKPoint(), point1.ToSKPoint(), point2.ToSKPoint(), point3.ToSKPoint()]; + } + } + else + { + if ((long)(point0.Y + _TextMeasure.Height) == (long)point1.Y) + { + // +---------------------------+ + // | | + // | 0-------------2 + // | | | + // 7--------1 4-------------3 + // | | | + // 6--------5 | + // | | + // | | + // | | + // +---------------------------+ + { + Point point2 = new(rhsVerticalLinePoint0.X - ps_CharsBetweenSections + selectionBoxXPadding, point0.Y); + Point point3 = new(rhsVerticalLinePoint0.X - ps_CharsBetweenSections + selectionBoxXPadding, point1.Y); + Point point4 = new(point0.X, point1.Y); + + points = [point0.ToSKPoint(), point2.ToSKPoint(), point3.ToSKPoint(), point4.ToSKPoint()]; + } + + path.AddPoly(points); + + { + Point point5 = new(point1.X, point1.Y + _TextMeasure.Height); + Point point6 = new(lhsVerticalLinePoint0.X + ps_CharsBetweenSections - selectionBoxXPadding, point1.Y + _TextMeasure.Height); + Point point7 = new(lhsVerticalLinePoint0.X + ps_CharsBetweenSections - selectionBoxXPadding, point1.Y); + points = [point1.ToSKPoint(), point5.ToSKPoint(), point6.ToSKPoint(), point7.ToSKPoint()]; + } + } + else + { + // +---------------------------+ + // | | + // | 0-------------2 + // | | | + // 6-------------7 | + // | | + // | 1------------------3 + // | | | + // 5--------4 | + // | | + // +---------------------------+ + Point point2 = new(rhsVerticalLinePoint0.X - ps_CharsBetweenSections + selectionBoxXPadding, point0.Y); + Point point3 = new(rhsVerticalLinePoint0.X - ps_CharsBetweenSections + selectionBoxXPadding, point1.Y); + Point point4 = new(point1.X, point1.Y + _TextMeasure.Height); + Point point5 = new(lhsVerticalLinePoint0.X + ps_CharsBetweenSections - selectionBoxXPadding, point1.Y + _TextMeasure.Height); + Point point6 = new(lhsVerticalLinePoint0.X + ps_CharsBetweenSections - selectionBoxXPadding, point0.Y + _TextMeasure.Height); + Point point7 = new(point0.X, point0.Y + _TextMeasure.Height); + + points = [point0.ToSKPoint(), point2.ToSKPoint(), point3.ToSKPoint(), point1.ToSKPoint(), point4.ToSKPoint(), point5.ToSKPoint(), point6.ToSKPoint(), point7.ToSKPoint()]; + } + } + + path.AddPoly(points); + if (brush is SolidColorBrush s) + pen.Color = s.Color.ToSKColor(); + Canvas.DrawPath(path, pen); + } + + private void DrawTextAccuracy(SKCanvas Canvas, SKPaint paint, SKPoint pt, string text) + { + //Canvas.DrawText(text, pt, paint); + int index = 0; + foreach (var c in text) + { + Canvas.DrawText(c.ToString(), pt.X + index * _TextMeasure.Width, pt.Y, paint); + index++; + } + } + + private void Canvas_PaintSurface(object sender, SKPaintSurfaceEventArgs e) + { + var view = sender as SKXamlCanvas; + var canvas = e.Surface.Canvas; + + if (_LinePaint == null) + { + _LinePaint = new() + { + IsStroke = true, + IsAntialias = true, + StrokeWidth = 1, + TextSize = (float)FontSize, + Typeface = _TextTypeFace, + TextAlign = SKTextAlign.Left, + }; + } + _LinePaint.Color = VerticalSeparatorLineBrush.Color.ToSKColor(); + + if (_TextPaint == null) + { + _TextPaint = new() + { + TextSize = (float)FontSize, + Typeface = _TextTypeFace, + TextScaleX = 1f, + IsAntialias = true, + TextAlign = SKTextAlign.Left, + HintingLevel = SKPaintHinting.Normal, + }; + } + + UpdateState(); + + if (DataSource != null) + { + canvas.Clear(); + long savedDataSourcePosition = DataSource.BaseStream.Position; + + DataSource.BaseStream.Position = Offset; + + if (ShowAddress) + { + var p0 = new Point(_AddressRect.Left, _AddressRect.Top).ToSKPoint(); + var p1 = new Point(_AddressRect.Right, _AddressRect.Bottom).ToSKPoint(); + + canvas.DrawLine(p0, p1, _LinePaint); + } + + if (ShowData) + { + var p0 = new Point(_DataRect.Left, _DataRect.Top).ToSKPoint(); + var p1 = new Point(_DataRect.Right, _DataRect.Bottom).ToSKPoint(); + + canvas.DrawLine(p0, p1, _LinePaint); + + if (HighlightedRegions.Count != 0 && MaxVisibleRows > 0 && Columns > 0) + { + var viewLimited = Offset + _BytesPerRow * MaxVisibleRows; + + foreach (var hlSection in HighlightedRegions) + { + if (hlSection.End <= Offset || (hlSection.Start >= viewLimited) || hlSection.Start >= hlSection.End) continue; + + var max_visible = Math.Min(hlSection.End, viewLimited); + + Point hlsP0 = ConvertOffsetToPosition(hlSection.Start, SelectionArea.Data); + Point hlsP1 = ConvertOffsetToPosition(max_visible, SelectionArea.Data); + + if (max_visible % _BytesPerRow == 0) + { + hlsP1.X = p1.X - _CharsBetweenSections * _TextMeasure.Width; + hlsP1.Y = Math.Max(hlsP0.Y, hlsP1.Y - _TextMeasure.Height); + } + else + { + hlsP1.X -= _TextMeasure.Width; + } + + DrawSelectionGeometry(canvas, hlSection.Color, _TextPaint, hlsP0, hlsP1, SelectionArea.Data); + } + } + } + + if (ShowText) + { + var p0 = new Point(_TextRect.Left, _TextRect.Top); + var p1 = new Point(_TextRect.Right, _TextRect.Bottom); + + canvas.DrawLine(p0.ToSKPoint(), p1.ToSKPoint(), _LinePaint); + + if (HighlightedRegions.Count != 0 && MaxVisibleRows > 0 && Columns > 0) + { + var viewLimited = Offset + MaxVisibleColumns * MaxVisibleRows; + + foreach (var hlSection in HighlightedRegions) + { + if (hlSection.End <= Offset || (hlSection.Start >= viewLimited) || hlSection.Start >= hlSection.End) continue; + + var max_visible = Math.Min(hlSection.End, viewLimited); + + Point hlsP0 = ConvertOffsetToPosition(hlSection.Start, SelectionArea.Text); + Point hlsP1 = ConvertOffsetToPosition(max_visible, SelectionArea.Text); + + if (max_visible % _BytesPerRow == 0) + { + hlsP1.X = p1.X - _CharsBetweenSections * _TextMeasure.Width; + hlsP1.Y = Math.Max(hlsP0.Y, hlsP1.Y - _TextMeasure.Height); + } + + DrawSelectionGeometry(canvas, hlSection.Color, _TextPaint, hlsP0, hlsP1, SelectionArea.Text); + } + } + } + + if (ShowData) + { + if (SelectionLength != 0 && MaxVisibleRows > 0 && Columns > 0) + { + Point sp0 = ConvertOffsetToPosition(SelectedOffset, SelectionArea.Data); + Point sp1 = ConvertOffsetToPosition(SelectedOffset + SelectionLength, SelectionArea.Data); + + if ((SelectedOffset + SelectionLength) % _BytesPerRow == 0) + { + sp1.X = _DataRect.Left - _CharsBetweenSections * _TextMeasure.Width; + sp1.Y = Math.Max(sp0.Y, sp1.Y - _TextMeasure.Height); + } + else + { + sp1.X -= _TextMeasure.Width; + } + + DrawSelectionGeometry(canvas, SelectionBrush, _TextPaint, sp0, sp1, SelectionArea.Data); + } + } + + if (ShowText) + { + if (SelectionLength != 0 && MaxVisibleRows > 0 && Columns > 0) + { + Point sp0 = ConvertOffsetToPosition(SelectedOffset, SelectionArea.Text); + Point sp1 = ConvertOffsetToPosition(SelectedOffset + SelectionLength, SelectionArea.Text); + + if ((SelectedOffset + SelectionLength) % _BytesPerRow == 0) + { + sp1.X = _TextRect.Left - _CharsBetweenSections * _TextMeasure.Width; + sp1.Y -= _TextMeasure.Height; + } + + DrawSelectionGeometry(canvas, SelectionBrush, _TextPaint, sp0, sp1, SelectionArea.Text); + } + } + + SKPoint origin = default; + origin.Y = _TextMeasure.Height * 3 / 4; /* left bottom to right top */ + + for (var row = 0; row < MaxVisibleRows; ++row) + { + if (ShowAddress) + { + if (DataSource.BaseStream.Position + _BytesPerColumn <= DataSource.BaseStream.Length) + { + var textToFormat = GetFormattedAddressText(Address + (ulong)DataSource.BaseStream.Position); + + if (AddressBrush is SolidColorBrush s) + { + _TextPaint.Color = s.Color.ToSKColor(); + } + canvas.DrawText(textToFormat, origin.X, origin.Y, _TextPaint); + + origin.X += (float)((CalculateAddressColumnCharWidth() + _CharsBetweenSections) * _TextMeasure.Width); + } + } + + long savedDataSourcePositionBeforeReadingData = DataSource.BaseStream.Position; + + if (ShowData) + { + origin.X += (float)(_CharsBetweenSections * _TextMeasure.Width); + + var cachedDataColumnCharWidth = CalculateDataColumnCharWidth(); + + // Needed to track text in alternating columns so we can use a different brush when drawing + var evenColumnBuilder = new StringBuilder(Columns * DataWidth); + var oddColumnBuilder = new StringBuilder(Columns * DataWidth); + + var column = 0; + + // Draw text up until selection start point + while (column < Columns) + { + if (DataSource.BaseStream.Position + _BytesPerColumn <= DataSource.BaseStream.Length) + { + if (DataSource.BaseStream.Position >= SelectedOffset) + { + break; + } + + var textToFormat = ReadFormattedData(); + + if (column % 2 == 0) + { + evenColumnBuilder.Append(textToFormat); + evenColumnBuilder.Append(' ', _CharsBetweenDataColumns); + + oddColumnBuilder.Append(' ', textToFormat.Length + _CharsBetweenDataColumns); + } + else + { + oddColumnBuilder.Append(textToFormat); + oddColumnBuilder.Append(' ', _CharsBetweenDataColumns); + + evenColumnBuilder.Append(' ', textToFormat.Length + _CharsBetweenDataColumns); + } + } + else + { + evenColumnBuilder.Append(' ', cachedDataColumnCharWidth + _CharsBetweenDataColumns); + oddColumnBuilder.Append(' ', cachedDataColumnCharWidth + _CharsBetweenDataColumns); + } + + ++column; + } + + { + if (Foreground is SolidColorBrush s) + { + _TextPaint.Color = s.Color.ToSKColor(); + } + DrawTextAccuracy(canvas, _TextPaint, origin, evenColumnBuilder.ToString()); + } + + { + if (AlternatingDataColumnTextBrush is SolidColorBrush s) + { + _TextPaint.Color = s.Color.ToSKColor(); + } + DrawTextAccuracy(canvas, _TextPaint, origin, oddColumnBuilder.ToString()); + } + origin.X += evenColumnBuilder.Length * _TextMeasure.Width; + + if (column < Columns) + { + // We'll reuse this builder for drawing selection text + evenColumnBuilder.Clear(); + + // Draw text starting from selection start point + while (column < Columns) + { + if (DataSource.BaseStream.Position + _BytesPerColumn <= DataSource.BaseStream.Length) + { + if (DataSource.BaseStream.Position >= SelectedOffset + SelectionLength) + { + break; + } + + var textToFormat = ReadFormattedData(); + + evenColumnBuilder.Append(textToFormat); + evenColumnBuilder.Append(' ', _CharsBetweenDataColumns); + } + else + { + evenColumnBuilder.Append(' ', cachedDataColumnCharWidth + _CharsBetweenDataColumns); + } + + ++column; + } + + { + if (SelectionTextBrush is SolidColorBrush s) + { + _TextPaint.Color = s.Color.ToSKColor(); + } + DrawTextAccuracy(canvas, _TextPaint, origin, evenColumnBuilder.ToString()); + } + + origin.X += evenColumnBuilder.Length * _TextMeasure.Width; + + if (column < Columns) + { + evenColumnBuilder.Clear(); + oddColumnBuilder.Clear(); + + // Draw text after end of selection + while (column < Columns) + { + if (DataSource.BaseStream.Position + _BytesPerColumn <= DataSource.BaseStream.Length) + { + var textToFormat = ReadFormattedData(); + if (column % 2 == 0) + { + evenColumnBuilder.Append(textToFormat); + evenColumnBuilder.Append(' ', _CharsBetweenDataColumns); + + oddColumnBuilder.Append(' ', textToFormat.Length + _CharsBetweenDataColumns); + } + else + { + oddColumnBuilder.Append(textToFormat); + oddColumnBuilder.Append(' ', _CharsBetweenDataColumns); + + evenColumnBuilder.Append(' ', textToFormat.Length + _CharsBetweenDataColumns); + } + } + else + { + evenColumnBuilder.Append(' ', cachedDataColumnCharWidth + _CharsBetweenDataColumns); + oddColumnBuilder.Append(' ', cachedDataColumnCharWidth + _CharsBetweenDataColumns); + } + + ++column; + } + + { + if (Foreground is SolidColorBrush s) + { + _TextPaint.Color = s.Color.ToSKColor(); + } + DrawTextAccuracy(canvas, _TextPaint, origin, evenColumnBuilder.ToString()); + } + + { + if (AlternatingDataColumnTextBrush is SolidColorBrush s) + { + _TextPaint.Color = s.Color.ToSKColor(); + } + DrawTextAccuracy(canvas, _TextPaint, origin, oddColumnBuilder.ToString()); + } + + origin.X += oddColumnBuilder.Length * _TextMeasure.Width; + } + } + + // Compensate for the extra space added at the end of the builder + origin.X += (float)((_CharsBetweenSections - _CharsBetweenDataColumns) * _TextMeasure.Width); + } + + if (ShowText) + { + origin.X += (float)(_CharsBetweenSections * _TextMeasure.Width); + + if (ShowData) + { + // Reset the stream to read one byte at a time + DataSource.BaseStream.Position = savedDataSourcePositionBeforeReadingData; + } + + var builder = new StringBuilder(Columns * DataWidth); + + var column = 0; + + // Draw text up until selection start point + while (column < Columns) + { + if (DataSource.BaseStream.Position + _BytesPerColumn <= DataSource.BaseStream.Length) + { + if (DataSource.BaseStream.Position >= SelectedOffset) + { + break; + } + + var textToFormat = ReadFormattedText(); + builder.Append(textToFormat); + } + + ++column; + } + + { + if (Foreground is SolidColorBrush s) + { + _TextPaint.Color = s.Color.ToSKColor(); + } + DrawTextAccuracy(canvas, _TextPaint, origin, builder.ToString()); + } + + if (column < Columns) + { + origin.X += builder.Length * _TextMeasure.Width; + + builder.Clear(); + + // Draw text starting from selection start point + while (column < Columns) + { + if (DataSource.BaseStream.Position + _BytesPerColumn <= DataSource.BaseStream.Length) + { + if (DataSource.BaseStream.Position >= SelectedOffset + SelectionLength) + { + break; + } + + var textToFormat = ReadFormattedText(); + builder.Append(textToFormat); + } + + ++column; + } + + { + if (SelectionTextBrush is SolidColorBrush s) + { + _TextPaint.Color = s.Color.ToSKColor(); + } + + DrawTextAccuracy(canvas, _TextPaint, origin, builder.ToString()); + } + + if (column < Columns) + { + origin.X += builder.Length * _TextMeasure.Width; + + builder.Clear(); + + // Draw text after end of selection + while (column < Columns) + { + if (DataSource.BaseStream.Position + _BytesPerColumn <= DataSource.BaseStream.Length) + { + var textToFormat = ReadFormattedText(); + builder.Append(textToFormat); + } + + ++column; + } + + { + if (Foreground is SolidColorBrush s) + { + _TextPaint.Color = s.Color.ToSKColor(); + } + + DrawTextAccuracy(canvas, _TextPaint, origin, builder.ToString()); + } + } + } + } + + origin.X = 0; + origin.Y += _TextMeasure.Height; + } + + DataSource.BaseStream.Position = savedDataSourcePosition; + } + } + + /// + /// Scrolls the contents of the control to the specified offset. + /// + /// + /// + /// The offset to scroll to. + /// + public void ScrollToOffset(long offset) + { + long maxBytesDisplayed = _BytesPerRow * MaxVisibleRows; + long lastByteOffset = (DataSource?.BaseStream?.Length ?? 1) - 1; + + // Adjust requested offset if not existing + if (offset < 0) + { + offset = 0; + } + else if (offset > lastByteOffset) + { + offset = lastByteOffset; + } + + if (Offset > offset) + { + // We need to scroll up + Offset -= ((Offset - offset - 1) / _BytesPerRow + 1) * _BytesPerRow; + } + + if (Offset + maxBytesDisplayed <= offset) + { + // We need to scroll down + Offset += ((offset - (Offset + maxBytesDisplayed)) / _BytesPerRow + 1) * _BytesPerRow; + } + } + + // Using .HasFlag(x) to correctly detect state of modifier keys (CTRL, SHIFT, ...) + private static bool IsKeyDown(VirtualKey key) => InputKeyboardSource.GetKeyStateForCurrentThread(key).HasFlag(CoreVirtualKeyStates.Down); + + /// + protected override void OnKeyDown(KeyRoutedEventArgs e) + { + base.OnKeyDown(e); + + // Context Menu + switch (e.Key) + { + case VirtualKey.Application: + { + ShowContextMenu(); + e.Handled = true; + return; + } + + case VirtualKey.F10: + { + if (IsKeyDown(VirtualKey.LeftShift) || IsKeyDown(VirtualKey.RightShift)) + { + ShowContextMenu(); + } + + e.Handled = true; + return; + } + } + + // Other keys + if (Columns > 0 && MaxVisibleRows > 0) + { + switch (e.Key) + { + case VirtualKey.A: + { + if (IsKeyDown(VirtualKey.LeftControl) || IsKeyDown(VirtualKey.RightControl)) + { + if (SelectAllCanExecute(null)) + { + SelectionStart = 0; + SelectionEnd = DataSource.BaseStream.Length; + } + } + + e.Handled = true; + break; + } + + case VirtualKey.C: + { + if (IsKeyDown(VirtualKey.LeftControl) || IsKeyDown(VirtualKey.RightControl)) + { + if (CopyCanExecute(null)) + { + if (IsKeyDown(VirtualKey.LeftShift) || IsKeyDown(VirtualKey.RightShift)) + { + // Copy text + Copy(true); + } + else + { + // Copy data + Copy(false); + } + } + } + + e.Handled = true; + break; + } + + case VirtualKey.Down: + { + if (IsKeyDown(VirtualKey.LeftShift) || IsKeyDown(VirtualKey.RightShift)) + { + SelectionEnd += _BytesPerRow; + } + else + { + SelectionStart += _BytesPerRow; + SelectionEnd = SelectionStart + _BytesPerColumn; + } + + ScrollToOffset(SelectionEnd - _BytesPerColumn); + + e.Handled = true; + + break; + } + + case VirtualKey.End: + { + if (IsKeyDown(VirtualKey.LeftControl) || IsKeyDown(VirtualKey.RightControl)) + { + SelectionEnd = DataSource.BaseStream.Length; + + if (!IsKeyDown(VirtualKey.LeftShift) && !IsKeyDown(VirtualKey.RightShift)) + { + SelectionStart = SelectionEnd - _BytesPerColumn; + } + + ScrollToOffset(SelectionEnd - _BytesPerColumn); + } + else + { + SelectionEnd += (Offset - SelectionEnd).Mod(_BytesPerRow); + + if (!IsKeyDown(VirtualKey.LeftShift) && !IsKeyDown(VirtualKey.RightShift)) + { + SelectionStart = SelectionEnd - _BytesPerColumn; + } + + ScrollToOffset(SelectionEnd - _BytesPerColumn); + } + + e.Handled = true; + + break; + } + + case VirtualKey.Home: + { + if (IsKeyDown(VirtualKey.LeftControl) || IsKeyDown(VirtualKey.RightControl)) + { + SelectionEnd = 0; + + if (!IsKeyDown(VirtualKey.LeftShift) && !IsKeyDown(VirtualKey.RightShift)) + { + SelectionStart = SelectionEnd; + SelectionEnd = SelectionStart + _BytesPerColumn; + } + + ScrollToOffset(SelectionEnd - _BytesPerColumn); + } + else + { + // TODO: Because of the way we represent selection there is no way to distinguish at the + // moment whether the selection ends at the start of the current line or the end of the + // previous line. As such, when the Shift+End hotkey is used twice consecutively a whole + // new line above the current selection will be selected. This is undesirable behavior + // that deviates from the canonical semantics of Shift+End. + SelectionEnd -= (SelectionEnd - 1 - Offset).Mod(_BytesPerRow) + 1; + + if (!IsKeyDown(VirtualKey.LeftShift) && !IsKeyDown(VirtualKey.RightShift)) + { + SelectionStart = SelectionEnd; + SelectionEnd = SelectionStart + _BytesPerColumn; + } + + ScrollToOffset(SelectionEnd - _BytesPerColumn); + } + + e.Handled = true; + + break; + } + + case VirtualKey.Left: + { + if (IsKeyDown(VirtualKey.LeftShift) || IsKeyDown(VirtualKey.RightShift)) + { + SelectionEnd -= _BytesPerColumn; + } + else + { + SelectionStart -= _BytesPerColumn; + SelectionEnd = SelectionStart + _BytesPerColumn; + } + + ScrollToOffset(SelectionEnd - _BytesPerColumn); + + e.Handled = true; + + break; + } + + case VirtualKey.PageDown: + { + bool isOffsetVisibleBeforeSelectionChange = IsOffsetVisible(SelectionEnd); + + SelectionEnd += _BytesPerRow * MaxVisibleRows; + + if (!IsKeyDown(VirtualKey.LeftShift) && !IsKeyDown(VirtualKey.RightShift)) + { + SelectionStart = SelectionEnd - _BytesPerColumn; + } + + _ScrollBar.Value += MaxVisibleRows; + + OnVerticalScrollBarScroll(_ScrollBar, ScrollEventType.SmallIncrement, _ScrollBar.Value); + + e.Handled = true; + break; + } + + case VirtualKey.PageUp: + { + bool isOffsetVisibleBeforeSelectionChange = IsOffsetVisible(SelectionEnd); + + SelectionEnd -= _BytesPerRow * MaxVisibleRows; + + if (!IsKeyDown(VirtualKey.LeftShift) && !IsKeyDown(VirtualKey.RightShift)) + { + SelectionStart = SelectionEnd - _BytesPerColumn; + SelectionEnd = SelectionStart + _BytesPerColumn; + } + + _ScrollBar.Value -= MaxVisibleRows; + + OnVerticalScrollBarScroll(_ScrollBar, ScrollEventType.SmallIncrement, _ScrollBar.Value); + + e.Handled = true; + break; + } + + case VirtualKey.Right: + { + if (IsKeyDown(VirtualKey.LeftShift) || IsKeyDown(VirtualKey.RightShift)) + { + SelectionEnd += _BytesPerColumn; + } + else + { + SelectionStart += _BytesPerColumn; + SelectionEnd = SelectionStart + _BytesPerColumn; + } + + ScrollToOffset(SelectionEnd - _BytesPerColumn); + + e.Handled = true; + break; + } + + case VirtualKey.Up: + { + if (IsKeyDown(VirtualKey.LeftShift) || IsKeyDown(VirtualKey.RightShift)) + { + SelectionEnd -= _BytesPerRow; + } + else + { + SelectionStart -= _BytesPerRow; + SelectionEnd = SelectionStart + _BytesPerColumn; + } + + ScrollToOffset(SelectionEnd - _BytesPerColumn); + + e.Handled = true; + break; + } + } + } + } + + protected override void OnDoubleTapped(DoubleTappedRoutedEventArgs e) + { + Focus(FocusState.Programmatic); + e.Handled = true; + + if (e.PointerDeviceType == PointerDeviceType.Mouse) + { + OnMouseDoubleClick(e.GetPosition(_Canvas)); + } + } + + protected override void OnPointerPressed(PointerRoutedEventArgs e) + { + Focus(FocusState.Programmatic); + e.Handled = true; + + var pps = e.GetCurrentPoint(this).Properties; + if (pps != null) + { + if (pps.PointerUpdateKind == PointerUpdateKind.LeftButtonPressed) + { + OnMouseLeftButtonDown(e); + } + } + } + + protected override void OnPointerReleased(PointerRoutedEventArgs e) + { + base.OnPointerReleased(e); + var pps = e.GetCurrentPoint(this).Properties; + if (pps != null) + { + if (pps.PointerUpdateKind == PointerUpdateKind.LeftButtonReleased) + { + OnMouseLeftButtonUp(e); + } + } + } + + protected override void OnPointerCanceled(PointerRoutedEventArgs e) + { + base.OnPointerCanceled(e); + var pps = e.GetCurrentPoint(this).Properties; + if (pps != null) + { + if (pps.PointerUpdateKind == PointerUpdateKind.LeftButtonReleased) + { + OnMouseLeftButtonUp(e); + } + } + } + + protected override void OnPointerCaptureLost(PointerRoutedEventArgs e) + { + base.OnPointerCaptureLost(e); + } + + protected override void OnPointerExited(PointerRoutedEventArgs e) + { + base.OnPointerExited(e); + } + + protected override void OnPointerEntered(PointerRoutedEventArgs e) + { + base.OnPointerEntered(e); + } + + /// + protected override void OnPointerMoved(PointerRoutedEventArgs e) + { + base.OnPointerMoved(e); + + if (_HighlightState != SelectionArea.None) + { + var position = e.GetCurrentPoint(_Canvas).Position; + var currentMouseOverOffset = ConvertPositionToOffset(position); + + switch (_HighlightState) + { + case SelectionArea.Address: + { + if (currentMouseOverOffset >= SelectionStart) + { + SelectionEnd = currentMouseOverOffset + _BytesPerRow; + } + else + { + SelectionEnd = currentMouseOverOffset; + } + + // Adjust start point + if (SelectionStart > SelectionEnd && _pointerMoveSelectionAdjustment != SelectionAdjustment.Up) + { + // If moving up and SelectionStart was previously adjusted down or not adjusted, then set SelectionStart to end of row. + SelectionStart = SelectionStart + (_BytesPerRow - _BytesPerColumn); + _pointerMoveSelectionAdjustment = SelectionAdjustment.Up; + } + else if (SelectionStart < SelectionEnd && _pointerMoveSelectionAdjustment == SelectionAdjustment.Up) + { + // If moving down and SelectionStart was previously adjusted up, then set SelectionStart to start of row. + SelectionStart = SelectionStart - (_BytesPerRow - _BytesPerColumn); + _pointerMoveSelectionAdjustment = SelectionAdjustment.Down; + } + break; + } + case SelectionArea.Data: + case SelectionArea.Text: + { + if (currentMouseOverOffset >= SelectionStart) + { + SelectionEnd = currentMouseOverOffset + _BytesPerColumn; + } + else + { + SelectionEnd = currentMouseOverOffset; + } + break; + } + } + + // Move next row into view if selection goes out of view + if (position.Y > _AddressRect.Y + _AddressRect.Height) + { + ScrollToOffset(currentMouseOverOffset + _BytesPerRow); + } + else if (position.Y < _AddressRect.Y) + { + ScrollToOffset(currentMouseOverOffset - _BytesPerRow); + } + } + } + + /// + protected override void OnPointerWheelChanged(PointerRoutedEventArgs e) + { + base.OnPointerWheelChanged(e); + var Delta = e.GetCurrentPoint(this).Properties.MouseWheelDelta; + + var value = _ScrollBar.Value; + if (Delta < 0) + { + _ScrollBar.Value += _ScrollWheelScrollRows; + + OnVerticalScrollBarScroll(_ScrollBar, ScrollEventType.SmallIncrement, _ScrollBar.Value); + } + else + { + _ScrollBar.Value -= _ScrollWheelScrollRows; + + OnVerticalScrollBarScroll(_ScrollBar, ScrollEventType.SmallDecrement, _ScrollBar.Value); + } + } + + /// + private void OnMouseDoubleClick(Point position) + { + Point addressVerticalLinePoint0 = CalculateAddressVerticalLinePoint0(); + + if (position.X < addressVerticalLinePoint0.X) + { + _HighlightBegin = SelectionArea.Address; + _HighlightState = SelectionArea.Address; + + SelectionStart = ConvertPositionToOffset(position); + SelectionEnd = SelectionStart + _BytesPerRow; + } + } + + /// + private void OnMouseLeftButtonDown(PointerRoutedEventArgs e) + { + if (_HighlightState == SelectionArea.None && CapturePointer(e.Pointer)) + { + Point position = e.GetCurrentPoint(_Canvas).Position; + + Point addressVerticalLinePoint0 = CalculateAddressVerticalLinePoint0(); + Point dataVerticalLinePoint0 = CalculateDataVerticalLinePoint0(); + Point textVerticalLinePoint0 = CalculateTextVerticalLinePoint0(); + + if (position.X < addressVerticalLinePoint0.X) + { + _HighlightBegin = SelectionArea.Address; + _HighlightState = SelectionArea.Address; + } + else if (position.X < dataVerticalLinePoint0.X) + { + _HighlightBegin = SelectionArea.Data; + _HighlightState = SelectionArea.Data; + } + else if (position.X < textVerticalLinePoint0.X) + { + _HighlightBegin = SelectionArea.Text; + _HighlightState = SelectionArea.Text; + } + + if (_HighlightState != SelectionArea.None) + { + SelectionStart = ConvertPositionToOffset(position); + + SelectionEnd = SelectionStart + _BytesPerColumn; + } + } + } + + /// + private void OnMouseLeftButtonUp(PointerRoutedEventArgs e) + { + _HighlightState = SelectionArea.None; + + ReleasePointerCapture(e.Pointer); + } + + private static void OnPropertyChangedInvalidateVisual(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var HexBox = (HexBox)d; + + HexBox.Reflush(); + } + + private static void OnSelectionEndChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var HexBox = (HexBox)d; + + HexBox.Reflush(); + + HexBox.OnPropertyChanged(nameof(SelectionEnd)); + HexBox.OnPropertyChanged(nameof(SelectionLength)); + HexBox.OnPropertyChanged(nameof(SelectedOffset)); + HexBox.OnPropertyChanged(nameof(SelectedAddress)); + HexBox.OnPropertyChanged(nameof(IsSelectionActive)); + } + + private static void OnSelectionStartChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var HexBox = (HexBox)d; + + HexBox.Reflush(); + + HexBox.OnPropertyChanged(nameof(SelectionStart)); + HexBox.OnPropertyChanged(nameof(SelectionLength)); + HexBox.OnPropertyChanged(nameof(SelectedOffset)); + HexBox.OnPropertyChanged(nameof(SelectedAddress)); + HexBox.OnPropertyChanged(nameof(IsSelectionActive)); + } + + private static object CoerceColumns(DependencyObject d, object value) + { + var HexBox = (HexBox)d; + + if (HexBox.MaxVisibleColumns == 0) + { + return (int)value; + } + else + { + return Math.Min((int)value, HexBox.MaxVisibleColumns); + } + } + + private static object CoerceMaxVisibleColumns(DependencyObject d, object value) + { + return Math.Min((int)value, _MaxColumns); + } + + private static object CoerceMaxVisibleRows(DependencyObject d, object value) + { + return Math.Min((int)value, _MaxRows); + } + + private static object CoerceSelectionStart(DependencyObject d, object value) + { + var HexBox = (HexBox)d; + + if (HexBox.DataSource != null) + { + long selectionStart = (long)value; + + // Selection offset cannot start in the middle of the data width + selectionStart -= selectionStart % HexBox._BytesPerColumn; + + // Selection start cannot be at the end of the stream so adjust by data width number of bytes + value = selectionStart.Clamp(0, HexBox.DataSource.BaseStream.Length / HexBox._BytesPerColumn * HexBox._BytesPerColumn - HexBox._BytesPerColumn); + } + else + { + value = 0L; + } + + return value; + } + + private static object CoerceSelectionEnd(DependencyObject d, object value) + { + var HexBox = (HexBox)d; + + if (HexBox.DataSource != null) + { + long selectionEnd = (long)value; + + // Selection offset cannot start in the middle of the data width + selectionEnd -= selectionEnd % HexBox._BytesPerColumn; + + // Unlike selection start the selection end can be at the end of the stream + value = selectionEnd.Clamp(0, HexBox.DataSource.BaseStream.Length / HexBox._BytesPerColumn * HexBox._BytesPerColumn); + } + else + { + value = 0L; + } + + return value; + } + + private static object CoerceOffset(DependencyObject d, object value) + { + var HexBox = (HexBox)d; + + if (HexBox.DataSource != null) + { + long offset = (long)value; + + value = offset.Clamp(0, HexBox.DataSource.BaseStream.Length); + } + else + { + value = 0L; + } + + return value; + } + + private static void OnAddressChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var HexBox = (HexBox)d; + + HexBox.SelectionStart = 0; + HexBox.SelectionEnd = 0; + + HexBox.Reflush(); + + HexBox.OnPropertyChanged(nameof(Address)); + HexBox.OnPropertyChanged(nameof(SelectedAddress)); + } + + private static void OnDataTypeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var HexBox = (HexBox)d; + + switch (HexBox.DataType) + { + case DataType.Int_1: + HexBox.DataWidth = 1; + break; + case DataType.Int_2: + HexBox.DataWidth = 2; + break; + case DataType.Int_4: + HexBox.DataWidth = 4; + break; + case DataType.Int_8: + HexBox.DataWidth = 8; + break; + case DataType.Float_32: + HexBox.DataWidth = 4; + break; + case DataType.Float_64: + HexBox.DataWidth = 8; + break; + } + + HexBox.Reflush(); + } + + private static void OnDataSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var HexBox = (HexBox)d; + + HexBox.Offset = 0; + HexBox.SelectionStart = 0; + HexBox.SelectionEnd = 0; + + HexBox.Reflush(); + } + + private void Reflush() + { + if (_Canvas != null) + { + _Canvas.Invalidate(); + } + } + + private void OnPropertyChanged([CallerMemberName] string name = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); + } + + private string ReadFormattedText() + { + StringBuilder builder = new(DataWidth); + + switch (TextFormat) + { + case TextFormat.Ascii: + { + for (var k = 0; k < DataWidth; ++k) + { + byte value = DataSource.ReadByte(); + + if (value > 31 && value < 127) + { + builder.Append(Convert.ToChar(value)); + } + else + { + builder.Append('.'); + } + } + + break; + } + + default: + { + throw new InvalidOperationException($"Invalid {nameof(TextFormat)} value."); + } + } + + return builder.ToString(); + } + + private string ReadFormattedData() + { + string result; + + if (DataType < DataType.Float_32) + { + switch (DataFormat) + { + case DataFormat.Decimal: + { + if (DataSignedness == DataSignedness.Signed) + { + switch (DataType) + { + case DataType.Int_1: + { + result = $"{DataSource.ReadSByte():+#;-#;0}".PadLeft(4); + break; + } + + case DataType.Int_2: + { + result = $"{EndianBitConverter.Convert(DataSource.ReadInt16(), Endianness):+#;-#;0}".PadLeft(6); + break; + } + + case DataType.Int_4: + { + result = $"{EndianBitConverter.Convert(DataSource.ReadInt32(), Endianness):+#;-#;0}".PadLeft(11); + break; + } + + case DataType.Int_8: + { + result = $"{EndianBitConverter.Convert(DataSource.ReadInt64(), Endianness):+#;-#;0}".PadLeft(21); + break; + } + + default: + { + throw new InvalidOperationException($"Invalid {nameof(DataWidth)} value."); + } + } + } + else if (DataSignedness == DataSignedness.Unsigned) + { + switch (DataType) + { + case DataType.Int_1: + { + result = $"{DataSource.ReadByte()}".PadLeft(3); + break; + } + + case DataType.Int_2: + { + result = $"{EndianBitConverter.Convert(DataSource.ReadUInt16(), Endianness)}".PadLeft(5); + break; + } + + case DataType.Int_4: + { + result = $"{EndianBitConverter.Convert(DataSource.ReadUInt32(), Endianness)}".PadLeft(10); + break; + } + + case DataType.Int_8: + { + result = $"{EndianBitConverter.Convert(DataSource.ReadUInt64(), Endianness)}".PadLeft(20); + break; + } + + default: + { + throw new InvalidOperationException($"Invalid {nameof(DataWidth)} value."); + } + } + } + else + { + throw new InvalidOperationException($"Invalid {nameof(DataType)} value."); + } + } + break; + + case DataFormat.Hexadecimal: + { + switch (DataType) + { + case DataType.Int_1: + { + result = $"{DataSource.ReadByte(),0:X2}"; + break; + } + + case DataType.Int_2: + { + result = $"{EndianBitConverter.Convert(DataSource.ReadUInt16(), Endianness),0:X4}"; + break; + } + + case DataType.Int_4: + { + result = $"{EndianBitConverter.Convert(DataSource.ReadUInt32(), Endianness),0:X8}"; + break; + } + + case DataType.Int_8: + { + result = $"{EndianBitConverter.Convert(DataSource.ReadUInt64(), Endianness),0:X16}"; + break; + } + + default: + { + throw new InvalidOperationException($"Invalid {nameof(DataWidth)} value."); + } + } + + break; + } + + default: + { + throw new InvalidOperationException($"Invalid {nameof(DataFormat)} value."); + } + } + } + else + { + switch (DataType) + { + case DataType.Float_32: + { + var bytes = BitConverter.GetBytes(EndianBitConverter.Convert(DataSource.ReadUInt32(), Endianness)); + var value = BitConverter.ToSingle(bytes, 0); + result = $"{value:E08}".PadLeft(16); + break; + } + + case DataType.Float_64: + { + var bytes = BitConverter.GetBytes(EndianBitConverter.Convert(DataSource.ReadUInt64(), Endianness)); + var value = BitConverter.ToSingle(bytes, 0); + result = $"{value:E16}".PadLeft(24); + break; + } + + default: + { + throw new InvalidOperationException($"Invalid {nameof(DataWidth)} value."); + } + } + } + + return result; + } + + private void SelectAllExecuted(object sender) + { + SelectAll(); + } + + private void CopyExecuted(object sender) + { + Copy(false); + } + + private void CopyTextExecuted(object sender) + { + Copy(true); + } + + private bool SelectAllCanExecute(object sender) + { + return DataSource != null && (ShowData || ShowText); + } + + private bool CopyCanExecute(object sender) + { + return IsSelectionActive && (ShowData || ShowText); + } + + private void OnVerticalScrollBarValueChanged(object sender, RangeBaseValueChangedEventArgs e) + { + _LastVerticalScrollValue = e.OldValue; + } + + private void OnVerticalScrollBarScroll(object sender, ScrollEventArgs e) + { + long newOffset = (long)e.NewValue * _BytesPerRow; + + Offset = newOffset; + } + + private void OnVerticalScrollBarScroll(object sender, ScrollEventType type, double NewValue) + { + long newOffset = (long)NewValue * _BytesPerRow; + + Offset = newOffset; + } + + private string GetFormattedAddressText(ulong address) + { + string formattedAddressText; + + switch (AddressFormat) + { + case AddressFormat.Address16: + { + formattedAddressText = $"{address & 0xFFFF,0:X4}"; + break; + } + + case AddressFormat.Address24: + { + formattedAddressText = $"{address >> 16 & 0xFF,0:X2}:{address & 0xFFFF,0:X4}"; + break; + } + + case AddressFormat.Address32: + { + formattedAddressText = $"{address >> 16 & 0xFFFF,0:X4}:{address & 0xFFFF,0:X4}"; + break; + } + + case AddressFormat.Address48: + { + formattedAddressText = $"{address >> 32 & 0xFF,0:X4}:{address & 0xFFFFFFFF,0:X8}"; + break; + } + + case AddressFormat.Address64: + { + formattedAddressText = $"{address >> 32,0:X8}:{address & 0xFFFFFFFF,0:X8}"; + break; + } + + default: + { + throw new InvalidOperationException($"Invalid {nameof(AddressFormat)} value."); + } + } + + return formattedAddressText; + } + + private int CalculateAddressColumnCharWidth() + { + int addressColumnCharWidth; + + switch (AddressFormat) + { + case AddressFormat.Address16: + { + addressColumnCharWidth = 4; + break; + } + + case AddressFormat.Address24: + { + addressColumnCharWidth = 7; + break; + } + + case AddressFormat.Address32: + { + addressColumnCharWidth = 9; + break; + } + + case AddressFormat.Address48: + { + addressColumnCharWidth = 13; + break; + } + + case AddressFormat.Address64: + { + addressColumnCharWidth = 17; + break; + } + + default: + { + throw new InvalidOperationException($"Invalid {nameof(AddressFormat)} value."); + } + } + + return addressColumnCharWidth; + } + + private int CalculateDataColumnCharWidth() + { + int dataColumnCharWidth; + + if (DataType < DataType.Float_32) + { + switch (DataFormat) + { + case DataFormat.Decimal: + { + switch (DataSignedness) + { + case DataSignedness.Signed: + { + switch (DataType) + { + case DataType.Int_1: + { + dataColumnCharWidth = 4; + break; + } + + case DataType.Int_2: + { + dataColumnCharWidth = 6; + break; + } + + case DataType.Int_4: + { + dataColumnCharWidth = 11; + break; + } + + case DataType.Int_8: + { + dataColumnCharWidth = 21; + break; + } + + default: + { + throw new InvalidOperationException($"Invalid {nameof(DataWidth)} value."); + } + } + } + + break; + + case DataSignedness.Unsigned: + { + switch (DataType) + { + case DataType.Int_1: + { + dataColumnCharWidth = 3; + break; + } + + case DataType.Int_2: + { + dataColumnCharWidth = 5; + break; + } + + case DataType.Int_4: + { + dataColumnCharWidth = 10; + break; + } + + case DataType.Int_8: + { + dataColumnCharWidth = 20; + break; + } + + default: + { + throw new InvalidOperationException($"Invalid {nameof(DataWidth)} value."); + } + } + } + + break; + + default: + { + throw new InvalidOperationException($"Invalid {nameof(DataType)} value."); + } + } + } + + break; + + case DataFormat.Hexadecimal: + { + switch (DataWidth) + { + case 1: + case 2: + case 4: + case 8: + { + dataColumnCharWidth = 2 * DataWidth; + break; + } + + default: + { + throw new InvalidOperationException($"Invalid {nameof(DataWidth)} value."); + } + } + + break; + } + + default: + { + throw new InvalidOperationException($"Invalid {nameof(DataFormat)} value."); + } + } + } + else + { + switch (DataType) + { + case DataType.Float_32: + { + dataColumnCharWidth = 16; + break; + } + + case DataType.Float_64: + { + dataColumnCharWidth = 24; + break; + } + + default: + { + throw new InvalidOperationException($"Invalid {nameof(DataWidth)} value."); + } + } + } + return dataColumnCharWidth; + } + + private Point CalculateAddressVerticalLinePoint0() + { + Point point1 = default; + + if (ShowAddress) + { + point1.X = (CalculateAddressColumnCharWidth() + _CharsBetweenSections) * _TextMeasure.Width; + } + + return point1; + } + + private Point CalculateAddressVerticalLinePoint1() + { + Point point2 = default; + + if (ShowAddress) + { + point2.X = (CalculateAddressColumnCharWidth() + _CharsBetweenSections) * _TextMeasure.Width; + } + + point2.Y = Math.Min(_TextMeasure.Height * (MaxVisibleRows + 1), _Canvas.ActualHeight); + + return point2; + } + + private Point CalculateDataVerticalLinePoint0() + { + Point point1 = CalculateAddressVerticalLinePoint0(); + + if (ShowData) + { + point1.X += (_CharsBetweenSections + (CalculateDataColumnCharWidth() + _CharsBetweenDataColumns) * Columns - _CharsBetweenDataColumns + _CharsBetweenSections) * _TextMeasure.Width; + } + + return point1; + } + + private Point CalculateDataVerticalLinePoint1() + { + Point point2 = CalculateAddressVerticalLinePoint1(); + + if (ShowData) + { + point2.X += (_CharsBetweenSections + (CalculateDataColumnCharWidth() + _CharsBetweenDataColumns) * Columns - _CharsBetweenDataColumns + _CharsBetweenSections) * _TextMeasure.Width; + } + + return point2; + } + + private int CalculateTextColumnCharWidth() + { + return _BytesPerColumn; + } + + private Point CalculateTextVerticalLinePoint0() + { + Point point1 = CalculateDataVerticalLinePoint0(); + + if (ShowText) + { + point1.X += (_CharsBetweenSections + CalculateTextColumnCharWidth() * Columns + _CharsBetweenSections) * _TextMeasure.Width; + } + + return point1; + } + + private Point CalculateTextVerticalLinePoint1() + { + Point point2 = CalculateDataVerticalLinePoint1(); + + if (ShowText) + { + point2.X += (_CharsBetweenSections + CalculateTextColumnCharWidth() * Columns + _CharsBetweenSections) * _TextMeasure.Width; + } + + return point2; + } + + private void UpdateState() + { + UpdateMaxVisibleRowsAndColumns(); + UpdateScrollBar(); + UpdateColumnsLayout(); + } + + private void UpdateColumnsLayout() + { + var p0 = CalculateAddressVerticalLinePoint0(); + var p1 = CalculateAddressVerticalLinePoint1(); + _AddressRect = new(p0, p1); + + p0 = CalculateDataVerticalLinePoint0(); + p1 = CalculateDataVerticalLinePoint1(); + _DataRect = new(p0, p1); + + p0 = CalculateTextVerticalLinePoint0(); + p1 = CalculateTextVerticalLinePoint1(); + _TextRect = new(p0, p1); + } + + private void UpdateMaxVisibleRowsAndColumns() + { + int maxVisibleRows = 0; + int maxVisibleColumns = 0; + + if ((ShowAddress || ShowData || ShowText) && _Canvas != null) + { + { + SKRect cellSize = new(); + string bigChars = "0123456789abcdef ABCDEF"; + for (int i = 0; i < bigChars.Length; i++) + { + var s = bigChars.Substring(i, 1); + if (_TextPaint.ContainsGlyphs(s)) // if the font does not contain the glyph, then skip it + { + var rect = new SKRect(); + _TextPaint.MeasureText(s, ref rect); + cellSize.Union(rect); + } + } + _TextMeasure = cellSize; + } + + _TextMeasure.Bottom = _TextMeasure.Height; /* 2 * line font height */ + + maxVisibleRows = Math.Max(0, (int)(_Canvas.ActualHeight / _TextMeasure.Height)); + + if (ShowData || ShowText) + { + int charsPerRow = (int)(_Canvas.ActualWidth / _TextMeasure.Width); + + if (ShowAddress) + { + charsPerRow -= CalculateAddressColumnCharWidth() + 2 * _CharsBetweenSections; + } + + if (ShowData && ShowText) + { + charsPerRow -= 3 * _CharsBetweenSections; + } + + int charsPerColumn = 0; + + if (ShowData) + { + charsPerColumn += CalculateDataColumnCharWidth() + _CharsBetweenDataColumns; + } + + if (ShowText) + { + charsPerColumn += CalculateTextColumnCharWidth(); + } + + if (charsPerColumn != 0) + { + maxVisibleColumns = Math.Max(0, charsPerRow / charsPerColumn); + } + } + else + { + maxVisibleColumns = 0; + } + } + + MaxVisibleRows = maxVisibleRows; + MaxVisibleColumns = maxVisibleColumns; + + // Maximum visible rows has now changed and so we must update the maximum amount we should scroll by + _ScrollBar.LargeChange = maxVisibleRows; + } + + private void UpdateScrollBar() + { + if ((ShowAddress || ShowData || ShowText) && DataSource != null && Columns > 0 && MaxVisibleRows > 0) + { + long q = DataSource.BaseStream.Length / _BytesPerRow; + long r = DataSource.BaseStream.Length % _BytesPerRow; + + // Each scroll value represents a single drawn row + _ScrollBar.Maximum = q + (r > 0 ? 1 : 0) - MaxVisibleRows; + + // Adjust the scroll value based on the current offset + _ScrollBar.Value = Offset / _BytesPerRow; + + // Adjust again to compensate for residual bytes if the number of bytes between the start of the stream + // and the current offset is less than the number of bytes we can display per row + if (_ScrollBar.Value == 0 && Offset > 0) + { + ++_ScrollBar.Value; + } + } + else + { + _ScrollBar.Maximum = 0; + } + } + + private long ConvertPositionToOffset(Point position) + { + long offset = Offset; + + switch (_HighlightBegin) + { + case SelectionArea.Address: + { + // Clamp the Y coordinate to within the address region + position.Y = position.Y.Clamp(_AddressRect.Top, _AddressRect.Bottom); + + // Convert the Y coordinate to the row number + position.Y /= _TextMeasure.Height; + + if (position.Y >= MaxVisibleRows) + { + // Due to floating point rounding we may end up with exactly the maximum number of rows, so adjust to compensate + --position.Y; + } + + offset += _BytesPerRow * (long)position.Y; + } + + break; + + case SelectionArea.Data: + { + var pix_CharsBetweenSections = _CharsBetweenSections * _TextMeasure.Width; + + // Clamp the X coordinate to within the data region + position.X = position.X.Clamp(_AddressRect.Left + pix_CharsBetweenSections, _DataRect.Left - pix_CharsBetweenSections); + + // Normalize with respect to the data region + position.X -= _AddressRect.Left + pix_CharsBetweenSections; + + // Convert the X coordinate to the column number + position.X /= (CalculateDataColumnCharWidth() + _CharsBetweenDataColumns) * _TextMeasure.Width; + + if (position.X >= Columns) + { + // Due to floating point rounding we may end up with exactly the maximum number of columns, so adjust to compensate + --position.X; + } + + // Clamp the Y coordinate to within the data region + position.Y = position.Y.Clamp(_DataRect.Top, _DataRect.Bottom); + + // Convert the Y coordinate to the row number + position.Y /= _TextMeasure.Height; + + if (position.Y >= MaxVisibleRows) + { + // Due to floating point rounding we may end up with exactly the maximum number of rows, so adjust to compensate + --position.Y; + } + + offset += ((long)position.Y * Columns + (long)position.X) * _BytesPerColumn; + } + + break; + + case SelectionArea.Text: + { + var pix_CharsBetweenSections = _CharsBetweenSections * _TextMeasure.Width; + + // Clamp the X coordinate to within the text region + position.X = position.X.Clamp(_DataRect.Left + pix_CharsBetweenSections, _TextRect.Left - pix_CharsBetweenSections); + + // Normalize with respect to the text region + position.X -= _DataRect.Left + pix_CharsBetweenSections; + + // Convert the X coordinate to the column number + position.X /= CalculateTextColumnCharWidth() * _TextMeasure.Width; + + if (position.X >= Columns) + { + // Due to floating point rounding we may end up with exactly the maximum number of columns, so + // adjust to compensate + --position.X; + } + + // Clamp the Y coordinate to within the text region + position.Y = position.Y.Clamp(_TextRect.Top, _TextRect.Bottom); + + // Convert the Y coordinate to the row number + position.Y /= _TextMeasure.Height; + + if (position.Y >= MaxVisibleRows) + { + // Due to floating point rounding we may end up with exactly the maximum number of rows, so adjust to compensate + --position.Y; + } + + offset += ((long)position.Y * Columns + (long)position.X) * _BytesPerColumn; + } + + break; + + default: + { + throw new InvalidOperationException($"Invalid highlight state ${_HighlightState}"); + } + } + + return offset; + } + + private Point ConvertOffsetToPosition(long offset, SelectionArea relativeTo) + { + Point position = default; + + switch (relativeTo) + { + case SelectionArea.Data: + { + position.X = _AddressRect.Left + _CharsBetweenSections * _TextMeasure.Width; + position.Y = _AddressRect.Top; + + // Normalize requested offset to a zero based column + long normalizedColumn = (offset - Offset) / _BytesPerColumn; + + position.X += (normalizedColumn % Columns + Columns) % Columns * (CalculateDataColumnCharWidth() + _CharsBetweenDataColumns) * _TextMeasure.Width; + + if (normalizedColumn < 0) + { + // Negative normalized offset means the Y position is above the current offset. Because division + // rounds toward zero we need to compensate here. + position.Y += ((normalizedColumn + 1) / Columns - 1) * _TextMeasure.Height; + } + else + { + position.Y += normalizedColumn / Columns * _TextMeasure.Height; + } + } + + break; + + case SelectionArea.Text: + { + position.X = _DataRect.Left + _CharsBetweenSections * _TextMeasure.Width; + position.Y = _DataRect.Top; + + // Normalize requested offset to a zero based column + long normalizedColumn = (offset - Offset) / _BytesPerColumn; + + position.X += (normalizedColumn % Columns + Columns) % Columns * CalculateTextColumnCharWidth() * _TextMeasure.Width; + + if (normalizedColumn < 0) + { + // Negative normalized offset means the Y position is above the current offset. Because division + // rounds toward zero we need to compensate here. + position.Y += ((normalizedColumn + 1) / Columns - 1) * _TextMeasure.Height; + } + else + { + position.Y += normalizedColumn / Columns * _TextMeasure.Height; + } + } + + break; + + default: + { + throw new ArgumentException($"Invalid relative area {relativeTo}", nameof(relativeTo)); + } + } + + return position; + } + + private bool IsOffsetVisible(long offset) + { + long maxBytesDisplayed = _BytesPerRow * MaxVisibleRows; + + return Offset <= offset && Offset + maxBytesDisplayed >= offset; + } + + /// + /// Show the context menu programatical. + /// Invoked if Application key or SCHIFT+F10 is pressed. + /// + private void ShowContextMenu() + { + // Get offset for context menu + var lastVisibleOffset = Offset + (_BytesPerRow * MaxVisibleRows) - 1; + var offset = Math.Max(Math.Max(SelectionStart, SelectionEnd), Offset); + var palcementOffset = Math.Min(offset, lastVisibleOffset); + + // Show menu + if (ShowData) + { + _Canvas.ContextFlyout.ShowAt(_Canvas, new FlyoutShowOptions + { + Position = ConvertOffsetToPosition(palcementOffset, SelectionArea.Data), + }); + } + else if (ShowText) + { + _Canvas.ContextFlyout.ShowAt(_Canvas, new FlyoutShowOptions + { + Position = ConvertOffsetToPosition(palcementOffset, SelectionArea.Text), + }); + } + else + { + _Canvas.ContextFlyout.ShowAt(_Canvas, new FlyoutShowOptions + { + Position = new Point(0, 0), + }); + } + } + + /// + /// Initializes static members of the class. + /// + public HexBox() + { + DefaultStyleKey = typeof(HexBox); + } + } + +} diff --git a/src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/Library/EndianConvert/EndianBinaryReader.cs b/src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/Library/EndianConvert/EndianBinaryReader.cs new file mode 100644 index 0000000000..1ba0658bd7 --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/Library/EndianConvert/EndianBinaryReader.cs @@ -0,0 +1,231 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// +// 2020-... created by Filip Jeremic (fjeremic) as "HexView.Wpf". +// 2024-... republished by @hotkidfamily as "HexBox.WinUI". +// 2025 Included in PowerToys. (Branch master; commit 72dcf64dc858c693a7a16887004c8ddbab61fce7.) +// + +namespace RegistryPreviewUILib.HexBox.Library.EndianConvert +{ + using System; + using System.IO; + using System.Text; + + /// + /// Reads primitive data types as binary values in a specific encoding and endianness. + /// + public class EndianBinaryReader : BinaryReader + { + /// + /// Initializes a new instance of the class based on the specified stream, endianness, and using UTF-8 + /// encoding. + /// + /// + /// + /// The input stream. + /// + /// + /// + /// The endianness of the data in the input stream. + /// + /// + /// + /// The stream does not support reading, is null, or is already closed. + /// + public EndianBinaryReader(Stream input, Endianness endianness) + : this(input, endianness, Encoding.UTF8) + { + // Void + } + + /// + /// Initializes a new instance of the class based on the specified stream, endianness, and character + /// encoding. + /// + /// + /// + /// The input stream. + /// + /// + /// + /// The endianness of the data in the input stream. + /// + /// + /// + /// The character encoding to use. + /// + /// + /// + /// The stream does not support reading, is null, or is already closed. + /// + public EndianBinaryReader(Stream input, Endianness endianness, Encoding encoding) + : this(input, endianness, encoding, false) + { + // Void + } + + /// + /// Initializes a new instance of the class based on the specified stream, endianness, and character + /// encoding, and optionally leaves the stream open. + /// + /// + /// + /// The input stream. + /// + /// + /// + /// The endianness of the data in the input stream. + /// + /// + /// + /// The character encoding to use. + /// + /// + /// + /// true to leave the stream open after the object is disposed; false otherwise. + /// + /// + /// + /// The stream does not support reading, is null, or is already closed. + /// + public EndianBinaryReader(Stream input, Endianness endianness, Encoding encoding, bool leaveOpen) + : base(input, encoding, leaveOpen) + { + Endianness = endianness; + } + + /// + /// Gets the endianness of the data in the input stream. + /// + public Endianness Endianness + { + get; + } + + /// + /// Reads a decimal value from the current stream and advances the current position of the stream by sixteen bytes. + /// + /// + /// + /// A decimal value read from the current stream. + /// + public override decimal ReadDecimal() + { + throw new NotImplementedException(); + } + + /// + /// Reads an 8-byte floating point value from the current stream and advances the current position of the stream by eight bytes. + /// + /// + /// + /// An 8-byte floating point value read from the current stream. + /// + public override double ReadDouble() + { + throw new NotImplementedException(); + } + + /// + /// Reads a 2-byte signed integer from the current stream and advances the current position of the stream by two bytes. + /// + /// + /// + /// A 2-byte signed integer read from the current stream. + /// + public override short ReadInt16() + { + return EndianBitConverter.Convert(base.ReadInt16(), Endianness); + } + + /// + /// Reads a 4-byte signed integer from the current stream and advances the current position of the stream by four bytes. + /// + /// + /// + /// A 4-byte signed integer read from the current stream. + /// + public override int ReadInt32() + { + return EndianBitConverter.Convert(base.ReadInt32(), Endianness); + } + + /// + /// Reads an 8-byte signed integer from the current stream and advances the current position of the stream by eight bytes. + /// + /// + /// + /// An 8-byte signed integer read from the current stream. + /// + public override long ReadInt64() + { + return EndianBitConverter.Convert(base.ReadInt64(), Endianness); + } + + /// + /// Reads a 4-byte floating point value from the current stream and advances the current position of the stream by four bytes. + /// + /// + /// + /// A 4-byte floating point value read from the current stream. + /// + public override float ReadSingle() + { + throw new NotImplementedException(); + } + + /// + /// Reads a string from the current stream. The string is prefixed with the length, encoded as an integer seven bits at a time. + /// + /// + /// + /// The string being read. + /// + public override string ReadString() + { + throw new NotImplementedException(); + } + + /// + /// Reads a 2-byte unsigned integer from the current stream using little-endian encoding and advances the position of the stream by + /// two bytes. + /// + /// + /// + /// A 2-byte unsigned integer read from this stream. + /// + public override ushort ReadUInt16() + { + return EndianBitConverter.Convert(base.ReadUInt16(), Endianness); + } + + /// + /// Reads a 4-byte unsigned integer from the current stream using little-endian encoding and advances the position of the stream by + /// four bytes. + /// + /// + /// + /// A 4-byte unsigned integer read from this stream. + /// + public override uint ReadUInt32() + { + return EndianBitConverter.Convert(base.ReadUInt32(), Endianness); + } + + /// + /// Reads an 8-byte unsigned integer from the current stream using little-endian encoding and advances the position of the stream by + /// eight bytes. + /// + /// + /// + /// An 8-byte unsigned integer read from this stream. + /// + public override ulong ReadUInt64() + { + return EndianBitConverter.Convert(base.ReadUInt64(), Endianness); + } + } +} diff --git a/src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/Library/EndianConvert/EndianBitConverter.cs b/src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/Library/EndianConvert/EndianBitConverter.cs new file mode 100644 index 0000000000..dde0287c2d --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/Library/EndianConvert/EndianBitConverter.cs @@ -0,0 +1,186 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// +// 2020-... created by Filip Jeremic (fjeremic) as "HexView.Wpf". +// 2024-... republished by @hotkidfamily as "HexBox.WinUI". +// 2025 Included in PowerToys. (Branch master; commit 72dcf64dc858c693a7a16887004c8ddbab61fce7.) +// + +namespace RegistryPreviewUILib.HexBox.Library.EndianConvert +{ + using System; + + /// + /// Converts integral values to the native endianness of this computer architecture. + /// + public static class EndianBitConverter + { + /// + /// Gets the native endianness of this computer architecture. + /// + public static readonly Endianness NativeEndianness = BitConverter.IsLittleEndian ? Endianness.LittleEndian : Endianness.BigEndian; + + /// + /// Converts a value from the specified endianness to the native endianness. + /// + /// + /// + /// The value to convert. + /// + /// + /// + /// The endianness of . + /// + /// + /// + /// The value converted from the specified endianness to the native endianness (). + /// + public static ushort Convert(ushort value, Endianness endianness) + { + if (endianness == NativeEndianness) + { + return value; + } + else + { + unchecked + { + return (ushort)((value & 0x00FFU) << 8 | + (value & 0xFF00U) >> 8); + } + } + } + + /// + /// Converts a value from the specified endianness to the native endianness. + /// + /// + /// + /// The value to convert. + /// + /// + /// + /// The endianness of . + /// + /// + /// + /// The value converted from the specified endianness to the native endianness (). + /// + public static uint Convert(uint value, Endianness endianness) + { + if (endianness == NativeEndianness) + { + return value; + } + else + { + unchecked + { + return (value & 0x000000FFU) << 24 | + (value & 0xFF000000U) >> 24 | + (value & 0x0000FF00U) << 8 | + (value & 0x00FF0000U) >> 8; + } + } + } + + /// + /// Converts a value from the specified endianness to the native endianness. + /// + /// + /// + /// The value to convert. + /// + /// + /// + /// The endianness of . + /// + /// + /// + /// The value converted from the specified endianness to the native endianness (). + /// + public static ulong Convert(ulong value, Endianness endianness) + { + if (endianness == NativeEndianness) + { + return value; + } + else + { + unchecked + { + return (value & 0x00000000000000FFUL) << 56 | + (value & 0xFF00000000000000UL) >> 56 | + (value & 0x000000000000FF00UL) << 40 | + (value & 0x00FF000000000000UL) >> 40 | + (value & 0x0000000000FF0000UL) << 24 | + (value & 0x0000FF0000000000UL) >> 24 | + (value & 0x00000000FF000000UL) << 8 | + (value & 0x000000FF00000000UL) >> 8; + } + } + } + + /// + /// Converts a value from the specified endianness to the native endianness. + /// + /// + /// + /// The value to convert. + /// + /// + /// + /// The endianness of . + /// + /// + /// + /// The value converted from the specified endianness to the native endianness (). + /// + public static short Convert(short value, Endianness endianness) + { + return (short)Convert((ushort)value, endianness); + } + + /// + /// Converts a value from the specified endianness to the native endianness. + /// + /// + /// + /// The value to convert. + /// + /// + /// + /// The endianness of . + /// + /// + /// + /// The value converted from the specified endianness to the native endianness (). + /// + public static int Convert(int value, Endianness endianness) + { + return (int)Convert((uint)value, endianness); + } + + /// + /// Converts a value from the specified endianness to the native endianness. + /// + /// + /// + /// The value to convert. + /// + /// + /// + /// The endianness of . + /// + /// + /// + /// The value converted from the specified endianness to the native endianness (). + /// + public static long Convert(long value, Endianness endianness) + { + return (long)Convert((ulong)value, endianness); + } + } +} diff --git a/src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/Library/EndianConvert/Endianness.cs b/src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/Library/EndianConvert/Endianness.cs new file mode 100644 index 0000000000..7293e8b9d1 --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/Library/EndianConvert/Endianness.cs @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// +// 2020-... created by Filip Jeremic (fjeremic) as "HexView.Wpf". +// 2024-... republished by @hotkidfamily as "HexBox.WinUI". +// 2025 Included in PowerToys. (Branch master; commit 72dcf64dc858c693a7a16887004c8ddbab61fce7.) +// + +namespace RegistryPreviewUILib.HexBox.Library.EndianConvert +{ + /// + /// Represents the endianness of a value in a computer architecture. + /// + public enum Endianness + { + /// + /// Most significant byte first. + /// + BigEndian, + + /// + /// Least significant byte first. + /// + LittleEndian, + } +} diff --git a/src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/Library/EndianConvert/FileFormatException.cs b/src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/Library/EndianConvert/FileFormatException.cs new file mode 100644 index 0000000000..81b96c3858 --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/Library/EndianConvert/FileFormatException.cs @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// +// 2020-... created by Filip Jeremic (fjeremic) as "HexView.Wpf". +// 2024-... republished by @hotkidfamily as "HexBox.WinUI". +// 2025 Included in PowerToys. (Branch master; commit 72dcf64dc858c693a7a16887004c8ddbab61fce7.) +// + +namespace RegistryPreviewUILib.HexBox.Library.EndianConvert +{ + using System; + + /// + /// The exception that is thrown when an input file or a data stream is malformed. + /// + [Serializable] + public sealed class FileFormatException : Exception + { + /// + /// Initializes a new instance of the class. + /// + public FileFormatException() + { + // Void + } + + /// + /// Initializes a new instance of the class with a specified error message. + /// + /// + /// + /// The message that describes the error. + /// + public FileFormatException(string message) + : base(message) + { + // Void + } + + /// + /// Initializes a new instance of the class with a specified error message and a reference to the inner + /// exception that is the cause of this exception. + /// + /// + /// + /// The message that describes the error. + /// + /// + /// + /// The exception that is the cause of the current exception, or a null reference if no inner exception is specified. + /// + public FileFormatException(string message, Exception innerException) + : base(message, innerException) + { + // Void + } + } +} diff --git a/src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/TextFormat.cs b/src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/TextFormat.cs new file mode 100644 index 0000000000..ca5cc99683 --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/TextFormat.cs @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// +// 2020-... created by Filip Jeremic (fjeremic) as "HexView.Wpf". +// 2024-... republished by @hotkidfamily as "HexBox.WinUI". +// 2025 Included in PowerToys. (Branch master; commit 72dcf64dc858c693a7a16887004c8ddbab61fce7.) +// + +namespace RegistryPreviewUILib.HexBox +{ + /// + /// Enumerates the text section encodings/formats that the control is able to display. + /// + public enum TextFormat + { + /// + /// Display data in ASCII (ISO-8859-1) encoding. + /// + Ascii, + } +} diff --git a/src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/Themes/Generic.xaml b/src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/Themes/Generic.xaml new file mode 100644 index 0000000000..8b2208128d --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/Themes/Generic.xaml @@ -0,0 +1,170 @@ + + + + diff --git a/src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/TypeConverters.cs b/src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/TypeConverters.cs new file mode 100644 index 0000000000..bed7841cab --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/TypeConverters.cs @@ -0,0 +1,264 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// +// 2020-... created by Filip Jeremic (fjeremic) as "HexView.Wpf". +// 2024-... republished by @hotkidfamily as "HexBox.WinUI". +// 2025 Included in PowerToys. (Branch master; commit 72dcf64dc858c693a7a16887004c8ddbab61fce7.) +// +#pragma warning disable SA1210 // Using directives should be ordered alphabetically by namespace +#pragma warning disable SA1208 // System using directives should be placed before other using directives +using RegistryPreviewUILib.HexBox.Library.EndianConvert; +using Microsoft.UI.Xaml.Data; +using System; +#pragma warning restore SA1208 // System using directives should be placed before other using directives +#pragma warning restore SA1210 // Using directives should be ordered alphabetically by namespace + +namespace RegistryPreviewUILib.HexBox +{ + public partial class HexboxDataTypeConverter : IValueConverter + { + /// + /// Convert a DataType value to its negation. + /// + /// The value to negate. + /// The type of the target property, as a type reference. + /// Optional parameter. Not used. + /// The language of the conversion. Not used + /// The value to be passed to the target dependency property. + public object Convert(object value, Type targetType, object parameter, string language) + { + if (value is DataType b) + { + if (parameter is string c) + { + return c == b.ToString(); + } + } + throw new NotImplementedException(); + } + + /// + /// Convert back a DataType value to its negation. + /// + /// The value to negate. + /// The type of the target property, as a type reference. + /// Optional parameter. Not used. + /// The language of the conversion. Not used + /// The value to be passed to the target dependency property. + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + if (value is bool b && parameter is string c) + { + if(c == "Int_1") + { + return DataType.Int_1; + } + else if (c == "Int_2") + { + return DataType.Int_2; + } + else if (c == "Int_4") + { + return DataType.Int_4; + } + else if (c == "Int_8") + { + return DataType.Int_8; + } + else if (c == "Float_32") + { + return DataType.Float_32; + } + else /*if (c == "Float_64")*/ + { + return DataType.Float_64; + } + } + throw new NotImplementedException(); + } + } + + public class HexboxDataSignednessConverter : IValueConverter + { + /// + /// Convert a DataSignedness value to its negation. + /// + /// The value to negate. + /// The type of the target property, as a type reference. + /// Optional parameter. Not used. + /// The language of the conversion. Not used + /// The value to be passed to the target dependency property. + public object Convert(object value, Type targetType, object parameter, string language) + { + if (value is DataSignedness b) + { + if (parameter is string c) + { + var end = c == "Signed" ? DataSignedness.Signed : DataSignedness.Unsigned; + return (b == end); + } + } + throw new NotImplementedException(); + } + + /// + /// Convert back a DataSignedness value to its negation. + /// + /// The value to negate. + /// The type of the target property, as a type reference. + /// Optional parameter. Not used. + /// The language of the conversion. Not used + /// The value to be passed to the target dependency property. + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + if (value is bool b && parameter is string c) + { + var end = c == "Signed" ? DataSignedness.Signed : DataSignedness.Unsigned; + if (b) + { + return end; + } + else + { + return c == "Signed" ? DataSignedness.Unsigned : DataSignedness.Signed; + } + } + throw new NotImplementedException(); + } + } + + public class HexboxDataFormatBoolConverter : IValueConverter + { + /// + /// Convert a DataFormat value to its negation. + /// + /// The value to negate. + /// The type of the target property, as a type reference. + /// Optional parameter. Not used. + /// The language of the conversion. Not used + /// The value to be passed to the target dependency property. + public object Convert(object value, Type targetType, object parameter, string language) + { + if(value is DataFormat f) + { + return f != DataFormat.Hexadecimal; + } + throw new NotImplementedException(); + } + + /// + /// Convert back a DataFormat value to its negation. + /// + /// The value to negate. + /// The type of the target property, as a type reference. + /// Optional parameter. Not used. + /// The language of the conversion. Not used + /// The value to be passed to the target dependency property. + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + throw new NotImplementedException(); + } + } + + + public class HexboxDataFormatConverter : IValueConverter + { + /// + /// Convert a DataFormat value to its negation. + /// + /// The value to negate. + /// The type of the target property, as a type reference. + /// Optional parameter. Not used. + /// The language of the conversion. Not used + /// The value to be passed to the target dependency property. + public object Convert(object value, Type targetType, object parameter, string language) + { + if (value is DataFormat b) + { + if (parameter is string c) + { + var end = c == "Decimal" ? DataFormat.Decimal: DataFormat.Hexadecimal; + return (b == end); + } + } + throw new NotImplementedException(); + } + + /// + /// Convert back a DataFormat value to its negation. + /// + /// The value to negate. + /// The type of the target property, as a type reference. + /// Optional parameter. Not used. + /// The language of the conversion. Not used + /// The value to be passed to the target dependency property. + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + if (value is bool b && parameter is string c) + { + var end = c == "Decimal" ? DataFormat.Decimal : DataFormat.Hexadecimal; + if (b) + { + return end; + } + else + { + return end == DataFormat.Decimal ? DataFormat.Hexadecimal : DataFormat.Decimal; + } + } + throw new NotImplementedException(); + } + } + + + public class BigEndianConverter : IValueConverter + { + /// + /// Convert a Endian value to its negation. + /// + /// The value to negate. + /// The type of the target property, as a type reference. + /// Optional parameter. Not used. + /// The language of the conversion. Not used + /// The value to be passed to the target dependency property. + public object Convert(object value, Type targetType, object parameter, string language) + { + if (value is Endianness b) + { + if (parameter is string c) + { + var end = c == "BigEndian" ? Endianness.BigEndian : Endianness.LittleEndian; + return (b == end); + } + } + throw new NotImplementedException(); + } + + /// + /// Convert back a Endian value to its negation. + /// + /// The value to negate. + /// The type of the target property, as a type reference. + /// Optional parameter. Not used. + /// The language of the conversion. Not used + /// The value to be passed to the target dependency property. + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + if (value is bool b && parameter is string c) + { + var end = c == "BigEndian" ? Endianness.BigEndian : Endianness.LittleEndian; + if (b) + { + return end; + } + else + { + return end == Endianness.BigEndian ? Endianness.LittleEndian : Endianness.BigEndian; + } + } + throw new NotImplementedException(); + } + } +} diff --git a/src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/Utilities.cs b/src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/Utilities.cs new file mode 100644 index 0000000000..d2e4795038 --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/Utilities.cs @@ -0,0 +1,81 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// +// 2020-... created by Filip Jeremic (fjeremic) as "HexView.Wpf". +// 2024-... republished by @hotkidfamily as "HexBox.WinUI". +// 2025 Included in PowerToys. (Branch master; commit 72dcf64dc858c693a7a16887004c8ddbab61fce7.) +// + +namespace RegistryPreviewUILib.HexBox +{ + using System; + + /// + /// A utility class with miscellaneous methods. + /// + internal static class Utilities + { + /// + /// Clamps the to the range [, ]. + /// + /// + /// + /// The type of the value to clamp. + /// + /// + /// + /// The value to clamp. + /// + /// + /// + /// The upper bound on the clamped value. + /// + /// + /// + /// The lower bound on the clmaped value. + /// + /// + /// + /// The nearest value of in the range [, + /// ]. + /// + public static T Clamp(this T value, T min, T max) + where T : IComparable + { + return value.CompareTo(min) < 0 ? min : value.CompareTo(max) > 0 ? max : value; + } + + /// + /// Calculates the arithmetic modulus of modulo . + /// + /// + /// + /// The type of the values. + /// + /// + /// + /// The value to compute the modulus of. + /// + /// + /// + /// The modulus. + /// + /// + /// + /// The non-negative value r such that for some integral value q: + /// = q*m + r. + /// + public static T Mod(this T n, T m) + where T : IComparable + { + dynamic dn = n; + dynamic dm = m; + + dynamic dr = dn % dm; + + return dr.CompareTo(0) < 0 ? dr + dm : dr; + } + } +} diff --git a/src/modules/registrypreview/RegistryPreviewUILib/MonacoEditorControl.xaml b/src/modules/registrypreview/RegistryPreviewUILib/Controls/MonacoEditor/MonacoEditorControl.xaml similarity index 100% rename from src/modules/registrypreview/RegistryPreviewUILib/MonacoEditorControl.xaml rename to src/modules/registrypreview/RegistryPreviewUILib/Controls/MonacoEditor/MonacoEditorControl.xaml diff --git a/src/modules/registrypreview/RegistryPreviewUILib/MonacoEditorControl.xaml.cs b/src/modules/registrypreview/RegistryPreviewUILib/Controls/MonacoEditor/MonacoEditorControl.xaml.cs similarity index 100% rename from src/modules/registrypreview/RegistryPreviewUILib/MonacoEditorControl.xaml.cs rename to src/modules/registrypreview/RegistryPreviewUILib/Controls/MonacoEditor/MonacoEditorControl.xaml.cs diff --git a/src/modules/registrypreview/RegistryPreviewUILib/MonacoHelper.cs b/src/modules/registrypreview/RegistryPreviewUILib/Controls/MonacoEditor/MonacoHelper.cs similarity index 100% rename from src/modules/registrypreview/RegistryPreviewUILib/MonacoHelper.cs rename to src/modules/registrypreview/RegistryPreviewUILib/Controls/MonacoEditor/MonacoHelper.cs diff --git a/src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.DataPreview.cs b/src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.DataPreview.cs new file mode 100644 index 0000000000..5cdffda4dd --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.DataPreview.cs @@ -0,0 +1,341 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.IO; +using System.Threading.Tasks; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Automation; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media; +using Microsoft.Windows.ApplicationModel.Resources; +using Windows.Foundation.Metadata; +using HB = RegistryPreviewUILib.HexBox; + +namespace RegistryPreviewUILib +{ + public sealed partial class RegistryPreviewMainPage : Page + { + private static bool _isDataPreviewHexBoxLoaded; + + internal async Task ShowExtendedDataPreview(string name, string type, string value) + { + // Create dialog + _isDataPreviewHexBoxLoaded = false; + var panel = new StackPanel() + { + Spacing = 16, + Padding = new Thickness(0), + }; + ContentDialog contentDialog = new ContentDialog() + { + Title = resourceLoader.GetString("DataPreviewTitle") + " - " + name, + Content = panel, + CloseButtonText = resourceLoader.GetString("DataPreviewClose"), + DefaultButton = ContentDialogButton.Primary, + Padding = new Thickness(0), + }; + contentDialog.Opened += ExtendedDataPreview_Opened; + + // Add content based on value type + switch (type) + { + case "REG_DWORD": + case "REG_QWORD": + AddHexView(ref panel, ref resourceLoader, value); + break; + case "REG_NONE": + case "REG_BINARY": + // Convert value to BinaryReader + byte[] byteArray = Convert.FromHexString(value.Replace(" ", string.Empty)); + MemoryStream memoryStream = new MemoryStream(byteArray); + BinaryReader binaryData = new BinaryReader(memoryStream); + binaryData.ReadBytes(byteArray.Length); + + // Convert value to text + // For more printable asci characters the following code lines are required: + // var cpW1252 = CodePagesEncodingProvider.Instance.GetEncoding(1252); + // || b == 128 || (b >= 130 && b <= 140) || b == 142 || (b >= 145 & b <= 156) || b >= 158 + // cpW1252.GetString([b]); + string binaryDataText = string.Empty; + foreach (byte b in byteArray) + { + // ASCII codes: + // 9, 10, 13: Space, Line Feed, Carriage Return + // 32-126: Printable characters + // 128, 130-140, 142, 145-156, 158-255: Extended printable characters + if (b == 9 || b == 10 || b == 13 || (b >= 32 && b <= 126)) + { + binaryDataText += Convert.ToChar(b); + } + } + + // Add controls + AddBinaryView(ref panel, ref resourceLoader, ref binaryData, binaryDataText); + break; + case "REG_MULTI_SZ": + var multiLineBox = new TextBox() + { + IsReadOnly = true, + AcceptsReturn = true, + TextWrapping = TextWrapping.NoWrap, + MaxHeight = 200, + FontSize = 14, + RequestedTheme = panel.ActualTheme, + Text = value, + }; + ScrollViewer.SetVerticalScrollBarVisibility(multiLineBox, ScrollBarVisibility.Auto); + ScrollViewer.SetHorizontalScrollBarVisibility(multiLineBox, ScrollBarVisibility.Auto); + AutomationProperties.SetName(multiLineBox, resourceLoader.GetString("DataPreview_AutomationPropertiesName_MultilineTextValue")); + panel.Children.Add(multiLineBox); + break; + case "REG_EXPAND_SZ": + AddExpandStringView(ref panel, ref resourceLoader, value); + break; + default: // REG_SZ + var stringBox = new TextBox() + { + IsReadOnly = true, + FontSize = 14, + RequestedTheme = panel.ActualTheme, + Text = value, + }; + AutomationProperties.SetName(stringBox, resourceLoader.GetString("DataPreview_AutomationPropertiesName_TextValue")); + panel.Children.Add(stringBox); + break; + } + + // Use this code to associate the dialog to the appropriate AppWindow by setting + // the dialog's XamlRoot to the same XamlRoot as an element that is already present in the AppWindow. + if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) + { + contentDialog.XamlRoot = this.Content.XamlRoot; + } + + // Show dialog and wait. + ChangeCursor(gridPreview, false); + _ = await contentDialog.ShowAsync(); + } + + private static void AddHexView(ref StackPanel panel, ref ResourceLoader resourceLoader, string value) + { + var hexBox = new TextBox() + { + Header = resourceLoader.GetString("DataPreviewHex"), + IsReadOnly = true, + FontSize = 14, + RequestedTheme = panel.ActualTheme, + Text = value.Split(" ")[0], + }; + var decimalBox = new TextBox() + { + Header = resourceLoader.GetString("DataPreviewDec"), + IsReadOnly = true, + FontSize = 14, + RequestedTheme = panel.ActualTheme, + Text = value.Split(" ")[1].TrimStart('(').TrimEnd(')'), + }; + panel.Children.Add(hexBox); + panel.Children.Add(decimalBox); + } + + private static void AddBinaryView(ref StackPanel panel, ref ResourceLoader resourceLoader, ref BinaryReader data, string dataText) + { + // Create SelectorBar + var navBar = new SelectorBar() + { + RequestedTheme = panel.ActualTheme, + }; + navBar.SelectionChanged += BinaryPreview_SelectorChanged; + navBar.Items.Add(new SelectorBarItem() + { + Text = resourceLoader.GetString("DataPreviewDataView"), + Tag = "DataView", + FontSize = 14, + RequestedTheme = panel.ActualTheme, + IsSelected = true, + }); + navBar.Items.Add(new SelectorBarItem() + { + Text = resourceLoader.GetString("DataPreviewVisibleText"), + Tag = "TextView", + FontSize = 14, + RequestedTheme = panel.ActualTheme, + IsSelected = false, + IsEnabled = !string.IsNullOrWhiteSpace(dataText), + }); + + // Create HexBox + var binaryPreviewBox = new HB.HexBox() + { + Height = 300, + Width = 495, + ShowAddress = true, + ShowData = true, + ShowText = true, + Columns = 8, + FontSize = 13, + RequestedTheme = panel.ActualTheme, + AddressBrush = (SolidColorBrush)Application.Current.Resources["AccentTextFillColorPrimaryBrush"], + AlternatingDataColumnTextBrush = (SolidColorBrush)Application.Current.Resources["TextFillColorSecondaryBrush"], + SelectionTextBrush = (SolidColorBrush)Application.Current.Resources["HexBox_SelectionTextBrush"], + SelectionBrush = (SolidColorBrush)Application.Current.Resources["HexBox_SelectionBackgroundBrush"], + VerticalSeparatorLineBrush = (SolidColorBrush)Application.Current.Resources["HexBox_VerticalLineBrush"], + BorderBrush = (LinearGradientBrush)Application.Current.Resources["HexBox_ControlBorderBrush"], + BorderThickness = (Thickness)Application.Current.Resources["HexBox_ControlBorderThickness"], + CornerRadius = (CornerRadius)Application.Current.Resources["ControlCornerRadius"], + DataFormat = HB.DataFormat.Hexadecimal, + DataSignedness = HB.DataSignedness.Unsigned, + DataType = HB.DataType.Int_1, + EnforceProperties = true, + Visibility = Visibility.Collapsed, + DataSource = data, + }; + AutomationProperties.SetName(binaryPreviewBox, resourceLoader.GetString("DataPreview_AutomationPropertiesName_BinaryDataPreview")); + binaryPreviewBox.Loaded += BinaryPreview_HexBoxLoaded; + binaryPreviewBox.GotFocus += BinaryPreview_HexBoxFocused; + binaryPreviewBox.LostFocus += BinaryPreview_HexBoxFocusLost; + + // Create TextBox + var visibleText = new TextBox() + { + IsReadOnly = true, + AcceptsReturn = true, + TextWrapping = TextWrapping.Wrap, + Height = 300, + Width = 495, + FontSize = 13, + Text = dataText, + RequestedTheme = panel.ActualTheme, + Visibility = Visibility.Collapsed, + }; + AutomationProperties.SetName(visibleText, resourceLoader.GetString("DataPreview_AutomationPropertiesName_VisibleTextPreview")); + + // Add controls: 0 = SelectorBar, 1 = ProgressRing, 2 = HexBox, 3 = TextBox + panel.Children.Add(navBar); + panel.Children.Add(new ProgressRing()); + panel.Children.Add(binaryPreviewBox); + panel.Children.Add(visibleText); + } + + private static void AddExpandStringView(ref StackPanel panel, ref ResourceLoader resourceLoader, string value) + { + var stringBoxRaw = new TextBox() + { + Header = resourceLoader.GetString("DataPreviewRawValue"), + IsReadOnly = true, + FontSize = 14, + RequestedTheme = panel.ActualTheme, + Text = value, + }; + var stringBoxExp = new TextBox() + { + Header = resourceLoader.GetString("DataPreviewExpandedValue"), + IsReadOnly = true, + FontSize = 14, + RequestedTheme = panel.ActualTheme, + Text = Environment.ExpandEnvironmentVariables(value), + }; + panel.Children.Add(stringBoxRaw); + panel.Children.Add(stringBoxExp); + } + + private static void BinaryPreview_SelectorChanged(SelectorBar sender, SelectorBarSelectionChangedEventArgs args) + { + // Child controls: 0 = SelectorBar, 1 = ProgressRing, 2 = HexBox, 3 = TextBox + var stackPanel = sender.Parent as StackPanel; + var progressRing = (ProgressRing)stackPanel.Children[1]; + var hexBox = (HB.HexBox)stackPanel.Children[2]; + var textBox = (TextBox)stackPanel.Children[3]; + + if (sender.SelectedItem.Tag.ToString() == "DataView") + { + textBox.Visibility = Visibility.Collapsed; + if (_isDataPreviewHexBoxLoaded) + { + progressRing.Visibility = Visibility.Collapsed; + hexBox.Visibility = Visibility.Visible; + + // Clear selection aligned to TextBox + hexBox.ClearSelection(); + hexBox.Focus(FocusState.Programmatic); + } + else + { + hexBox.Visibility = Visibility.Collapsed; + progressRing.Visibility = Visibility.Visible; + progressRing.Focus(FocusState.Programmatic); + } + } + else + { + progressRing.Visibility = Visibility.Collapsed; + + hexBox.Visibility = Visibility.Collapsed; + textBox.Visibility = Visibility.Visible; + + // Workaround for wrong text selection (color) after switching back to "Visible text" + textBox.Focus(FocusState.Programmatic); + textBox.Select(0, 0); + } + } + + private static void BinaryPreview_HexBoxLoaded(object sender, RoutedEventArgs e) + { + _isDataPreviewHexBoxLoaded = true; + + // Child controls: 0 = SelectorBar, 1 = ProgressRing, 2 = HexBox, 3 = TextBox + var hexBox = (HB.HexBox)sender; + var stackPanel = hexBox.Parent as StackPanel; + var selectorBar = stackPanel.Children[0] as SelectorBar; + var progressRing = stackPanel.Children[1] as ProgressRing; + + if (selectorBar.SelectedItem.Tag.ToString() == "DataView") + { + progressRing.Visibility = Visibility.Collapsed; + hexBox.Visibility = Visibility.Visible; + } + } + + /// + /// Event handler to set correct control border if focused. + /// + private static void BinaryPreview_HexBoxFocused(object sender, RoutedEventArgs e) + { + var hexBox = (HB.HexBox)sender; + + hexBox.BorderThickness = (Thickness)Application.Current.Resources["HexBox_ControlBorderFocusedThickness"]; + hexBox.BorderBrush = (LinearGradientBrush)Application.Current.Resources["HexBox_ControlBorderFocusedBrush"]; + } + + /// + /// Event handler to set correct control border if not focused. + /// + private static void BinaryPreview_HexBoxFocusLost(object sender, RoutedEventArgs e) + { + var hexBox = (HB.HexBox)sender; + + // Workaround: Verify that the newly focused control isn't the context menu of the HexBox control + if (FocusManager.GetFocusedElement(hexBox.XamlRoot).GetType() != typeof(MenuFlyoutPresenter)) + { + hexBox.BorderThickness = (Thickness)Application.Current.Resources["HexBox_ControlBorderThickness"]; + hexBox.BorderBrush = (LinearGradientBrush)Application.Current.Resources["HexBox_ControlBorderBrush"]; + } + } + + /// + /// Make sure that for REG_Binary preview the HexBox control is focused after opening. + /// + private static void ExtendedDataPreview_Opened(ContentDialog sender, ContentDialogOpenedEventArgs e) + { + // If <_isDataPreviewHexBoxLoaded == true> then we have the right content on the dialog. + if (_isDataPreviewHexBoxLoaded) + { + // Child controls: 0 = SelectorBar, 1 = ProgressRing, 2 = HexBox, 3 = TextBox + (sender.Content as StackPanel).Children[2].Focus(FocusState.Programmatic); + } + } + } +} diff --git a/src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.Events.cs b/src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.Events.cs index 20009533c1..c8f72837f1 100644 --- a/src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.Events.cs +++ b/src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.Events.cs @@ -8,7 +8,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; - using CommunityToolkit.WinUI.UI.Controls; using Microsoft.UI.Dispatching; using Microsoft.UI.Xaml; @@ -415,5 +414,48 @@ namespace RegistryPreviewUILib saveButton.IsEnabled = true; }); } + + // Commands to show data preview + public void ButtonExtendedPreview_Click(object sender, RoutedEventArgs e) + { + var data = ((Button)sender).DataContext as RegistryValue; + InvokeExtendedDataPreview(data); + } + + public void MenuExtendedPreview_Click(object sender, RoutedEventArgs e) + { + var data = ((MenuFlyoutItem)sender).DataContext as RegistryValue; + InvokeExtendedDataPreview(data); + } + + private async void InvokeExtendedDataPreview(RegistryValue valueData) + { + // Only one content dialog can be open at the same time and multiple instances of data preview can crash the app. + if (_dialogSemaphore.CurrentCount == 0) + { + return; + } + + try + { + // Lock ui and request dialog lock + _dialogSemaphore.Wait(); + ChangeCursor(gridPreview, true); + + await ShowExtendedDataPreview(valueData.Name, valueData.Type, valueData.Value); + } + catch + { +#if DEBUG + throw; +#endif + } + finally + { + // Unblock ui and release dialog lock + ChangeCursor(gridPreview, false); + _dialogSemaphore.Release(); + } + } } } diff --git a/src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.Utilities.cs b/src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.Utilities.cs index 3ec275abf8..57d27fdc12 100644 --- a/src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.Utilities.cs +++ b/src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.Utilities.cs @@ -503,6 +503,7 @@ namespace RegistryPreviewUILib case "REG_NONE": if (value.Length <= 0) { + registryValue.IsEmptyBinary = true; value = resourceLoader.GetString("ZeroLength"); } else diff --git a/src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.xaml b/src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.xaml index 18225902e3..50a89fc223 100644 --- a/src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.xaml +++ b/src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.xaml @@ -2,6 +2,7 @@ x:Class="RegistryPreviewUILib.RegistryPreviewMainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:converter="using:CommunityToolkit.WinUI.Converters" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="using:RegistryPreviewUILib" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" @@ -10,6 +11,12 @@ xmlns:ui="using:CommunityToolkit.WinUI" mc:Ignorable="d"> + + + + + + - + @@ -225,10 +232,18 @@ Spacing="8"> - - + + + - - + + + @@ -274,20 +297,36 @@ + Orientation="Horizontal" + Spacing="6"> - - + + + +