├── .gitattributes ├── .gitignore ├── .nuke ├── build.schema.json └── parameters.json ├── .vscode ├── launch.json └── tasks.json ├── LICENSE.md ├── README.md ├── build.cmd ├── build.ps1 ├── build.sh ├── init.cmd ├── init.ps1 ├── init.sh ├── nuget.config ├── screenshot.png ├── src ├── .editorconfig ├── Directory.Build.props ├── Directory.Build.targets ├── Directory.Packages.props ├── Environment.props ├── NotepadBasedCalculator.App.sln ├── app │ ├── dev │ │ ├── Directory.Build.props │ │ ├── Directory.Build.targets │ │ ├── NotepadBasedCalculator.Api │ │ │ ├── AbstractSyntaxTree │ │ │ │ ├── AbstractSyntaxTreeBase.cs │ │ │ │ ├── BinaryOperatorExpression.cs │ │ │ │ ├── BinaryOperatorType.cs │ │ │ │ ├── DataExpression.cs │ │ │ │ ├── Expression.cs │ │ │ │ ├── FunctionExpression.cs │ │ │ │ ├── GroupExpression.cs │ │ │ │ ├── ReferenceExpression.cs │ │ │ │ ├── Statement.cs │ │ │ │ ├── VariableDeclarationStatement.cs │ │ │ │ └── VariableReferenceExpression.cs │ │ │ ├── Core │ │ │ │ ├── CultureHelper.cs │ │ │ │ ├── DictionaryExtensions.cs │ │ │ │ ├── DictionaryWithSpecialEnumValueConverter.cs │ │ │ │ ├── EnumExtension.cs │ │ │ │ ├── ExtensionOrderer.cs │ │ │ │ └── Threading │ │ │ │ │ ├── AsyncLazy.cs │ │ │ │ │ ├── CancellationTokenExtension.cs │ │ │ │ │ ├── DisposableSempahore.cs │ │ │ │ │ ├── TaskExtension.cs │ │ │ │ │ └── TaskSchedulerAwaiter.cs │ │ │ ├── Data │ │ │ │ ├── Data.cs │ │ │ │ ├── Definition │ │ │ │ │ ├── AngleData.cs │ │ │ │ │ ├── AreaData.cs │ │ │ │ │ ├── BooleanData.cs │ │ │ │ │ ├── CurrencyData.cs │ │ │ │ │ ├── CurrencyValue.cs │ │ │ │ │ ├── DateTimeData.cs │ │ │ │ │ ├── DecimalData.cs │ │ │ │ │ ├── DurationData.cs │ │ │ │ │ ├── FractionData.cs │ │ │ │ │ ├── InformationData.cs │ │ │ │ │ ├── LengthData.cs │ │ │ │ │ ├── MassData.cs │ │ │ │ │ ├── OrdinalData.cs │ │ │ │ │ ├── PercentageData.cs │ │ │ │ │ ├── SpeedData.cs │ │ │ │ │ ├── TemperatureData.cs │ │ │ │ │ └── VolumeData.cs │ │ │ │ ├── IData.cs │ │ │ │ ├── IDataParser.cs │ │ │ │ ├── IDecimal.cs │ │ │ │ ├── INumericData.cs │ │ │ │ ├── ISupportMultipleDataTypeForArithmeticOperation.cs │ │ │ │ └── IValueRelativeToOtherData.cs │ │ │ ├── DataOperationException.cs │ │ │ ├── Grammar │ │ │ │ ├── FunctionDefinition.cs │ │ │ │ ├── IFunctionDefinitionProvider.cs │ │ │ │ ├── IGrammarProvider.cs │ │ │ │ └── TokenDefinitionGrammar.cs │ │ │ ├── IArithmeticAndRelationOperationService.cs │ │ │ ├── ICurrencyService.cs │ │ │ ├── ILogger.cs │ │ │ ├── IMefProvider.cs │ │ │ ├── IncompatibleUnitsException.cs │ │ │ ├── Lexer │ │ │ │ ├── ILexer.cs │ │ │ │ ├── IToken.cs │ │ │ │ ├── ITokenEnumerator.cs │ │ │ │ ├── LinkedToken.cs │ │ │ │ ├── LinkedTokenExtensions.cs │ │ │ │ ├── PredefinedTokenNames.cs │ │ │ │ ├── Token.cs │ │ │ │ └── TokenizedTextLine.cs │ │ │ ├── Metadata │ │ │ │ ├── CultureAttribute.cs │ │ │ │ ├── CultureCodeMetadata.cs │ │ │ │ ├── FunctionInterpreterMetadata.cs │ │ │ │ ├── IOrderableMetadata.cs │ │ │ │ ├── NameAttribute.cs │ │ │ │ ├── OrderAttribute.cs │ │ │ │ ├── ParserAndInterpreterMetadata.cs │ │ │ │ └── SupportedCultures.cs │ │ │ ├── NotepadBasedCalculator.Api.csproj │ │ │ ├── ParserAndInterpreter │ │ │ │ ├── ExpressionParserAndInterpreterResult.cs │ │ │ │ ├── IExpressionParserAndInterpreter.cs │ │ │ │ ├── IFunctionInterpreter.cs │ │ │ │ ├── IParserAndInterpreterService.cs │ │ │ │ ├── IParserAndInterpretersRepository.cs │ │ │ │ ├── IStatementParserAndInterpreter.cs │ │ │ │ ├── IVariableService.cs │ │ │ │ ├── PredefinedExpressionParserAndInterpreterNames.cs │ │ │ │ ├── PredefinedStatementParserAndInterpreterNames.cs │ │ │ │ └── StatementParserAndInterpreterResult.cs │ │ │ └── UnsupportedArithmeticOperationException.cs │ │ ├── NotepadBasedCalculator.BuiltInPlugins │ │ │ ├── Data │ │ │ │ ├── BooleanDataParser.cs │ │ │ │ ├── CurrencyDataParser.cs │ │ │ │ ├── DateTimeDataParser.cs │ │ │ │ ├── NumberDataParser.cs │ │ │ │ ├── OrdinalDataParser.cs │ │ │ │ ├── PercentageDataParser.cs │ │ │ │ ├── TemperatureDataParser.cs │ │ │ │ ├── UnitDataParser.cs │ │ │ │ └── UnitMap.cs │ │ │ ├── ExpressionParsersAndInterpreters │ │ │ │ ├── Conditional │ │ │ │ │ └── ConditionalExpressionParserAndInterpreter.cs │ │ │ │ ├── Function │ │ │ │ │ └── FunctionExpressionParserAndInterpreter.cs │ │ │ │ └── Numerical │ │ │ │ │ ├── NumericalExpressionParserAndInterpreter.cs │ │ │ │ │ └── PrimitiveExpressionParserAndInterpreter.cs │ │ │ ├── Functions │ │ │ │ ├── General │ │ │ │ │ ├── MidpointInterpreter.cs │ │ │ │ │ ├── RandomNumberInterpreter.cs │ │ │ │ │ └── RemainderInterpreter.cs │ │ │ │ └── Percentage │ │ │ │ │ ├── IsPercentOfWhatInterpreter.cs │ │ │ │ │ ├── IsPercentOnWhatInterpreter.cs │ │ │ │ │ ├── IsWhatPercentOfInterpreter.cs │ │ │ │ │ ├── IsWhatPercentOffInterpreter.cs │ │ │ │ │ ├── IsWhatPercentOnInterpreter.cs │ │ │ │ │ ├── PercentOfInterpreter.cs │ │ │ │ │ ├── PercentOffInterpreter.cs │ │ │ │ │ └── PercentOnInterpreter.cs │ │ │ ├── Grammars │ │ │ │ ├── FunctionDefinitionProvider.cs │ │ │ │ ├── GrammarProvider.cs │ │ │ │ ├── SpecialTokenDefinition.json │ │ │ │ ├── UnitMapProvider.cs │ │ │ │ ├── en-us │ │ │ │ │ ├── FunctionDefinition.json │ │ │ │ │ ├── TokenDefinition.json │ │ │ │ │ └── UnitNames.json │ │ │ │ └── fr-fr │ │ │ │ │ └── UnitNames.json │ │ │ ├── NotepadBasedCalculator.BuiltInPlugins.csproj │ │ │ └── StatementParsersAndInterpreters │ │ │ │ ├── Comment │ │ │ │ ├── CommentStatement.cs │ │ │ │ └── CommentStatementParserAndInterpreter.cs │ │ │ │ ├── Condition │ │ │ │ ├── ConditionStatement.cs │ │ │ │ ├── ConditionStatementParserAndInterpreter.cs │ │ │ │ └── ConditionalExpressionStatementParserAndInterpreter.cs │ │ │ │ ├── Function │ │ │ │ └── FunctionExpressionStatementParserAndInterpreter.cs │ │ │ │ ├── Header │ │ │ │ ├── HeaderStatement.cs │ │ │ │ └── HeaderStatementParserAndInterpreter.cs │ │ │ │ ├── NumericalExpression │ │ │ │ ├── NumericalCalculusStatement.cs │ │ │ │ └── NumericalExpressionStatementParserAndInterpreter.cs │ │ │ │ └── VariableDeclaration │ │ │ │ └── VariableDeclarationStatementParserAndInterpreter.cs │ │ ├── NotepadBasedCalculator.Core │ │ │ ├── ArithmeticAndRelationOperationService.cs │ │ │ ├── Assets │ │ │ │ └── defaultExchangeRates.json │ │ │ ├── Core │ │ │ │ ├── CurrencyService.cs │ │ │ │ └── DescendingComparer.cs │ │ │ ├── Lexer.cs │ │ │ ├── Logger.cs │ │ │ ├── Mef │ │ │ │ ├── MefComposer.cs │ │ │ │ └── MefProvider.cs │ │ │ ├── NotepadBasedCalculator.Core.csproj │ │ │ ├── ParserAndInterpreter.cs │ │ │ ├── ParserAndInterpreterFactory.cs │ │ │ ├── ParserAndInterpreterResultLine.cs │ │ │ ├── ParserAndInterpreterResultUpdatedEventArgs.cs │ │ │ ├── ParserAndInterpreterService.cs │ │ │ ├── ParserRepository.cs │ │ │ ├── TextDocument.cs │ │ │ └── VariableService.cs │ │ ├── NotepadBasedCalculator.Desktop.Mac │ │ │ ├── AppDelegate.cs │ │ │ ├── Assets.xcassets │ │ │ │ ├── AppIcon.appiconset │ │ │ │ │ ├── AppIcon-128.png │ │ │ │ │ ├── AppIcon-128@2x.png │ │ │ │ │ ├── AppIcon-16.png │ │ │ │ │ ├── AppIcon-16@2x.png │ │ │ │ │ ├── AppIcon-256.png │ │ │ │ │ ├── AppIcon-256@2x.png │ │ │ │ │ ├── AppIcon-32.png │ │ │ │ │ ├── AppIcon-32@2x.png │ │ │ │ │ ├── AppIcon-512.png │ │ │ │ │ ├── AppIcon-512@2x.png │ │ │ │ │ └── Contents.json │ │ │ │ └── Contents.json │ │ │ ├── Entitlements.plist │ │ │ ├── Info.plist │ │ │ ├── NotepadBasedCalculator.Desktop.Mac.csproj │ │ │ ├── PlatformInitializer.cs │ │ │ ├── Program.cs │ │ │ ├── Properties │ │ │ │ └── AssemblyInfo.cs │ │ │ └── Services │ │ │ │ └── ThemeService.cs │ │ ├── NotepadBasedCalculator.Desktop.Windows │ │ │ ├── NotepadBasedCalculator.Desktop.Windows.csproj │ │ │ ├── PlatformInitializer.cs │ │ │ ├── Program.cs │ │ │ ├── Properties │ │ │ │ └── AssemblyInfo.cs │ │ │ └── Services │ │ │ │ └── ThemeService.cs │ │ ├── NotepadBasedCalculator.Desktop │ │ │ ├── App.axaml │ │ │ ├── App.axaml.cs │ │ │ ├── MainWindow.axaml │ │ │ ├── MainWindow.axaml.cs │ │ │ ├── NotepadBasedCalculator.Desktop.csproj │ │ │ └── Platform │ │ │ │ ├── IPlatformInitializer.cs │ │ │ │ └── Services │ │ │ │ ├── IUiService.cs │ │ │ │ └── Theme │ │ │ │ ├── AppTheme.cs │ │ │ │ ├── IThemeService.cs │ │ │ │ └── UserPreferredTheme.cs │ │ └── shared │ │ │ ├── GlobalUsings.cs │ │ │ ├── SharedAssemblyInfo.cs │ │ │ └── SharedAssemblyVersion.cs │ ├── tests │ │ ├── NotepadBasedCalculator.Core.Tests │ │ │ ├── AlgebraTests.cs │ │ │ ├── BinaryOperationTests.cs │ │ │ ├── DataParserTests.cs │ │ │ ├── ExpressionParsersTests.cs │ │ │ ├── ExtensionOrdererTests.cs │ │ │ ├── FunctionTests.cs │ │ │ ├── InterpreterTests.cs │ │ │ ├── LexerTests.cs │ │ │ ├── MefBaseTest.cs │ │ │ ├── NotepadBasedCalculator.Core.Tests.csproj │ │ │ ├── OperationHelperTests.cs │ │ │ ├── ParserTests.cs │ │ │ ├── StatementParsersTests.cs │ │ │ └── TestHelper.cs │ │ └── NotepadBasedCalculator.StandaloneConsoleTestApp │ │ │ ├── NotepadBasedCalculator.StandaloneConsoleTestApp.csproj │ │ │ └── Program.cs │ └── tools │ │ ├── Directory.Build.props │ │ └── NotepadBasedCalculator.Benchmark │ │ ├── CalculatorBenchmarks.cs │ │ ├── NotepadBasedCalculator.Benchmark.csproj │ │ └── Program.cs └── build │ ├── .editorconfig │ ├── AppVersion │ ├── AppVersion.cs │ ├── AppxManifestUpdater.cs │ ├── CSharpUpdater.cs │ ├── CSharpVersionUpdateRule.cs │ ├── VersionString.cs │ └── VersionUpdateRule.cs │ ├── Build.cs │ ├── Configuration.cs │ ├── Directory.Build.props │ ├── Directory.Build.targets │ ├── DotnetParameters.cs │ ├── PlatformTarget.cs │ ├── _build.csproj │ └── _build.csproj.DotSettings └── tools ├── Install-DotNet.ps1 ├── Install-DotNet.sh └── app-version-number.txt /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.nuke/parameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./build.schema.json", 3 | "Solution": "src/NotepadBasedCalculator.App.sln" 4 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | // Use IntelliSense to find out which attributes exist for C# debugging 6 | // Use hover for the description of the existing attributes 7 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 8 | "name": "Desktop macOS App (launch)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/bin/Debug/anycpu/NotepadBasedCalculator.Desktop.Mac/net6.0-macos/osx-x64/NotepadBasedCalculator.Desktop.Mac.app/Contents/MacOS/NotepadBasedCalculator.Desktop.Mac", 14 | "args": [], 15 | "cwd": "${workspaceFolder}", 16 | // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console 17 | "console": "internalConsole", 18 | "stopAtEntry": false 19 | }, 20 | { 21 | // Use IntelliSense to find out which attributes exist for C# debugging 22 | // Use hover for the description of the existing attributes 23 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 24 | "name": "Desktop Windows App (launch)", 25 | "type": "coreclr", 26 | "request": "launch", 27 | "preLaunchTask": "build", 28 | // If you have changed target frameworks, make sure to update the program path. 29 | "program": "${workspaceFolder}\\bin\\Debug\\AnyCPU\\NotepadBasedCalculator.Desktop.Windows\\net6.0-win10\\NotepadBasedCalculator.Desktop.Windows.exe", 30 | "args": [], 31 | "cwd": "${workspaceFolder}", 32 | // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console 33 | "console": "internalConsole", 34 | "stopAtEntry": false 35 | } 36 | ] 37 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "type": "shell", 7 | "command": "${workspaceFolder}/build.sh Compile --incremental-build true --configuration Debug", 8 | "windows": { 9 | "command": "${workspaceFolder}\\build.cmd Compile --incremental-build true --configuration Debug" 10 | } 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Etienne Baudoux 4 | 5 | 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: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | 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. -------------------------------------------------------------------------------- /build.cmd: -------------------------------------------------------------------------------- 1 | :; set -eo pipefail 2 | :; SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) 3 | :; ${SCRIPT_DIR}/build.sh "$@" 4 | :; exit $? 5 | 6 | @ECHO OFF 7 | powershell -ExecutionPolicy ByPass -NoProfile -File "%~dp0build.ps1" %* 8 | -------------------------------------------------------------------------------- /build.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | Param( 3 | [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)] 4 | [string[]]$BuildArguments 5 | ) 6 | 7 | function ExecSafe([scriptblock] $cmd) { 8 | & $cmd 9 | if ($LASTEXITCODE) { exit $LASTEXITCODE } 10 | } 11 | 12 | Set-StrictMode -Version 2.0; $ErrorActionPreference = "Stop"; $ConfirmPreference = "None"; trap { Write-Error $_ -ErrorAction Continue; exit 1 } 13 | $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent 14 | 15 | # Install .Net 16 | ExecSafe { & $PSScriptRoot\tools\Install-DotNet.ps1 -RootFolder $PSScriptRoot } 17 | 18 | # Build the builder project. 19 | Write-Host "Building the pipeline" 20 | $BuildProjectFile = "$PSScriptRoot\src\build\_build.csproj" 21 | 22 | ExecSafe { & $env:DOTNET_EXE build $BuildProjectFile /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet } 23 | Write-Host "Done." 24 | Write-Output "---------------------------------------" 25 | 26 | # Run the builder 27 | ExecSafe { & $env:DOTNET_EXE run --project $BuildProjectFile --no-build -- $BuildArguments } 28 | Write-Host "Done." 29 | Write-Output "---------------------------------------" 30 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eo pipefail 4 | SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) 5 | 6 | # Install .Net 7 | . "./tools/Install-DotNet.sh" $SCRIPT_DIR 8 | 9 | # Build the build project. 10 | echo "Building the pipeline" 11 | BUILD_PROJECT_FILE="$SCRIPT_DIR/src/build/_build.csproj" 12 | 13 | "$DOTNET_EXE" build "$BUILD_PROJECT_FILE" /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet 14 | echo "Done." 15 | echo "--------------------------------------" 16 | 17 | # Run the building 18 | "$DOTNET_EXE" run --project "$BUILD_PROJECT_FILE" --no-build -- "$@" 19 | echo "Done." 20 | echo "--------------------------------------" 21 | -------------------------------------------------------------------------------- /init.cmd: -------------------------------------------------------------------------------- 1 | :; set -eo pipefail 2 | :; SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) 3 | :; ${SCRIPT_DIR}/init.sh "$@" 4 | :; exit $? 5 | 6 | @ECHO OFF 7 | powershell -ExecutionPolicy ByPass -NoProfile -File "%~dp0init.ps1" %* 8 | -------------------------------------------------------------------------------- /init.ps1: -------------------------------------------------------------------------------- 1 | function ExecSafe([scriptblock] $cmd) { 2 | & $cmd 3 | if ($LASTEXITCODE) { exit $LASTEXITCODE } 4 | } 5 | 6 | Set-StrictMode -Version 2.0; $ErrorActionPreference = "Stop"; $ConfirmPreference = "None"; trap { Write-Error $_ -ErrorAction Continue; exit 1 } 7 | $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent 8 | 9 | # Install .Net 10 | ExecSafe { & $PSScriptRoot\tools\Install-DotNet.ps1 -RootFolder $PSScriptRoot } 11 | 12 | # Restore workloads 13 | Write-Host "Restoring all workloads" 14 | ExecSafe { & $env:DOTNET_EXE workload install macos -v:quiet } 15 | Get-ChildItem $PSScriptRoot\src\ -rec |? { $_.FullName.EndsWith('proj') -and ($_.FullName.Contains('Mac') -or $_.FullName.Contains('iOS') -or $_.FullName.Contains('Android') -or $_.FullName.Contains('Windows') -or $_.FullName.Contains('Linux')) } |% { 16 | $ProjectPath = $_.FullName; 17 | Write-Host "Restoring workload for $($ProjectPath)..." 18 | ExecSafe { & $env:DOTNET_EXE workload restore -v:quiet --project $ProjectPath } 19 | } 20 | Write-Host "Done." 21 | Write-Output "---------------------------------------" 22 | 23 | # Restore NuGet solution dependencies 24 | Write-Host "Restoring all dependencies" 25 | Get-ChildItem $PSScriptRoot\src\ -rec |? { $_.FullName.EndsWith('.sln') } |% { 26 | $SolutionPath = $_.FullName; 27 | Write-Host "Restoring packages for $($SolutionPath)..." 28 | ExecSafe { & $env:DOTNET_EXE restore -v:quiet $SolutionPath } 29 | } 30 | Write-Host "Done." 31 | Write-Output "---------------------------------------" -------------------------------------------------------------------------------- /init.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eo pipefail 4 | SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) 5 | 6 | # Install .Net 7 | . "./tools/Install-DotNet.sh" $SCRIPT_DIR 8 | 9 | # Restore workloads 10 | echo "Restoring all workloads" 11 | "$DOTNET_EXE" workload restore macos -v:quiet 12 | PROJECTS=$(find ./src/ -type f \( -name "*Mac.csproj" -o -iname "*Windows.csproj" -o -name "*iOS.csproj" -o -name "*Android.csproj" -o -name "*Linux.csproj" \) -print ) 13 | for PROJECT_FILE in $PROJECTS 14 | do 15 | echo "Restoring workload for $PROJECT_FILE..." 16 | "$DOTNET_EXE" workload restore -v:quiet --project $PROJECT_FILE 17 | done 18 | echo "Done." 19 | echo "---------------------------------------" 20 | 21 | # Restore NuGet solution dependencies 22 | echo "Restoring all dependencies" 23 | SOLUTIONS=$(find ./src/ -iname "*.sln" -print) 24 | for SOLUTION_FILE in $SOLUTIONS 25 | do 26 | echo "Restoring packages for $SOLUTION_FILE..." 27 | "$DOTNET_EXE" restore -v:quiet $SOLUTION_FILE 28 | done 29 | echo "Done." 30 | echo "---------------------------------------" 31 | -------------------------------------------------------------------------------- /nuget.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veler/notepad-based-calculator/0748213d0dae153dc8d736a90a0db74f5c4766bd/screenshot.png -------------------------------------------------------------------------------- /src/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 16.0 6 | 7 | netstandard2.0;netstandard2.1 8 | net6.0 9 | $(NetCore)-windows10.0.17763.0 10 | $(NetCore)-macos 11 | $(NetStandardVersion);$(NetCore) 12 | 13 | 14 | Debug 15 | Any CPU 16 | anycpu 17 | 10.0 18 | true 19 | 20 | 21 | $([System.IO.Path]::GetDirectoryName($([MSBuild]::GetPathOfFileAbove('.gitignore', '$(MSBuildThisFileDirectory)'))))\ 22 | $(RepoRoot)bin\$(Configuration)\$(Platform)\ 23 | $(BaseOutputPath)$(MSBuildProjectName)\ 24 | $(RepoRoot)obj\$(Platform)\$(MSBuildProjectName)\ 25 | $(BaseIntermediateOutputPath) 26 | $(BaseIntermediateOutputPath)Generated Files\ 27 | $(RepoRoot)packages\ 28 | 29 | 30 | true 31 | 32 | 33 | 34 | 16.0 35 | 36 | -------------------------------------------------------------------------------- /src/Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/Environment.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | $([MSBuild]::IsOSPlatform('Windows')) 4 | $([MSBuild]::IsOSPlatform('OSX')) 5 | $([MSBuild]::IsOSPlatform('Linux')) 6 | 7 | 8 | 9 | $(DefineConstants);MAC 10 | 11 | -------------------------------------------------------------------------------- /src/app/dev/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | enable 7 | enable 8 | false 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/app/dev/Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/AbstractSyntaxTree/AbstractSyntaxTreeBase.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | public abstract class AbstractSyntaxTreeBase 4 | { 5 | public LinkedToken FirstToken { get; } 6 | 7 | public LinkedToken LastToken { get; } 8 | 9 | protected AbstractSyntaxTreeBase(LinkedToken firstToken, LinkedToken lastToken) 10 | { 11 | Guard.IsNotNull(firstToken); 12 | Guard.IsNotNull(lastToken); 13 | FirstToken = firstToken; 14 | LastToken = lastToken; 15 | } 16 | 17 | /// 18 | /// Gets a string representation of the expression or statement. 19 | /// 20 | /// String that reprensents the expression or statement 21 | public abstract override string ToString(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/AbstractSyntaxTree/BinaryOperatorExpression.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | /// 4 | /// Represents a binary conditional expression 5 | /// 6 | public sealed class BinaryOperatorExpression : Expression 7 | { 8 | /// 9 | /// Gets or sets the left expression 10 | /// 11 | public Expression LeftExpression { get; } 12 | 13 | /// 14 | /// Gets the binary operator 15 | /// 16 | public BinaryOperatorType Operator { get; } 17 | 18 | /// 19 | /// Gets or sets the right expression 20 | /// 21 | public Expression RightExpression { get; } 22 | 23 | /// 24 | /// Initializes a new instance of the class. 25 | /// 26 | /// The left expression 27 | /// The binary operator 28 | /// The right expression 29 | public BinaryOperatorExpression(Expression leftExpression, BinaryOperatorType conditionalOperator, Expression rightExpression) 30 | : base(leftExpression.FirstToken, rightExpression.LastToken) 31 | { 32 | Guard.IsNotNull(leftExpression); 33 | Guard.IsNotNull(rightExpression); 34 | LeftExpression = leftExpression; 35 | Operator = conditionalOperator; 36 | RightExpression = rightExpression; 37 | } 38 | 39 | public override string ToString() 40 | { 41 | return $"({LeftExpression} {Operator.GetDescription()} {RightExpression})"; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/AbstractSyntaxTree/BinaryOperatorType.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | /// 4 | /// Defines identifiers for supported binary operators 5 | /// 6 | public enum BinaryOperatorType 7 | { 8 | /// 9 | /// Identity equal operator 10 | /// 11 | [Description("==")] 12 | Equality = 0, 13 | 14 | /// 15 | /// Identity no equal operator 16 | /// 17 | [Description("!=")] 18 | NoEquality = 1, 19 | 20 | /// 21 | /// Less than operator 22 | /// 23 | [Description("<")] 24 | LessThan = 2, 25 | 26 | /// 27 | /// Less than or equal operator 28 | /// 29 | [Description("<=")] 30 | LessThanOrEqualTo = 3, 31 | 32 | /// 33 | /// Greater than operator 34 | /// 35 | [Description(">")] 36 | GreaterThan = 4, 37 | 38 | /// 39 | /// Greater than or equal operator 40 | /// 41 | [Description(">=")] 42 | GreaterThanOrEqualTo = 5, 43 | 44 | /// 45 | /// Addition operator 46 | /// 47 | [Description("+")] 48 | Addition = 6, 49 | 50 | /// 51 | /// Subtraction operator 52 | /// 53 | [Description("-")] 54 | Subtraction = 7, 55 | 56 | /// 57 | /// Multiplication operator 58 | /// 59 | [Description("*")] 60 | Multiply = 8, 61 | 62 | /// 63 | /// Division operator 64 | /// 65 | [Description("/")] 66 | Division = 9, 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/AbstractSyntaxTree/DataExpression.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | public class DataExpression : ReferenceExpression 4 | { 5 | public IData Data { get; } 6 | 7 | public DataExpression(LinkedToken firstToken, LinkedToken lastToken, IData data) 8 | : base(firstToken, lastToken) 9 | { 10 | Guard.IsNotNull(data); 11 | Data = data; 12 | } 13 | 14 | public override string ToString() 15 | { 16 | return Data.ToString() ?? string.Empty; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/AbstractSyntaxTree/Expression.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | /// 4 | /// Basic class that represents an expression in a statement. 5 | /// 6 | public abstract class Expression : AbstractSyntaxTreeBase 7 | { 8 | protected Expression(LinkedToken firstToken, LinkedToken lastToken) 9 | : base(firstToken, lastToken) 10 | { 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/AbstractSyntaxTree/FunctionExpression.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | public sealed class FunctionExpression : Expression 4 | { 5 | public FunctionDefinition FunctionDefinition { get; } 6 | 7 | public FunctionExpression(FunctionDefinition functionDefinition, LinkedToken firstToken, LinkedToken lastToken) 8 | : base(firstToken, lastToken) 9 | { 10 | FunctionDefinition = functionDefinition; 11 | } 12 | 13 | public override string ToString() 14 | { 15 | return $"function {FunctionDefinition.FunctionFullName}()"; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/AbstractSyntaxTree/GroupExpression.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | /// 4 | /// Represents an expression between parenthesis. 5 | /// 6 | public sealed class GroupExpression : Expression 7 | { 8 | /// 9 | /// Gets or sets the expression in the group 10 | /// 11 | public Expression InnerExpression { get; } 12 | 13 | public GroupExpression(LinkedToken firstToken, LinkedToken lastToken, Expression innerExpression) 14 | : base(firstToken, lastToken) 15 | { 16 | Guard.IsNotNull(innerExpression); 17 | InnerExpression = innerExpression; 18 | } 19 | 20 | public override string ToString() 21 | { 22 | return $"({InnerExpression})"; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/AbstractSyntaxTree/ReferenceExpression.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | /// 4 | /// Basic class for a reference to something. 5 | /// 6 | public abstract class ReferenceExpression : Expression 7 | { 8 | protected ReferenceExpression(LinkedToken firstToken, LinkedToken lastToken) 9 | : base(firstToken, lastToken) 10 | { 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/AbstractSyntaxTree/Statement.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | /// 4 | /// Basic class that represents a statement. 5 | /// 6 | public abstract class Statement : AbstractSyntaxTreeBase 7 | { 8 | protected Statement(LinkedToken firstToken, LinkedToken lastToken) 9 | : base(firstToken, lastToken) 10 | { 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/AbstractSyntaxTree/VariableDeclarationStatement.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | public sealed class VariableDeclarationStatement : Statement 4 | { 5 | public string VariableName { get; } 6 | 7 | public LinkedToken VariableNameStart { get; } 8 | 9 | public LinkedToken VariableNameEnd { get; } 10 | 11 | public Expression AssignedValue { get; } 12 | 13 | public VariableDeclarationStatement(LinkedToken variableNameStart, LinkedToken variableNameEnd, string variableName, Expression assignedValue) 14 | : base(variableNameStart, assignedValue.LastToken) 15 | { 16 | Guard.IsNotNull(variableNameStart); 17 | Guard.IsNotNull(variableNameEnd); 18 | Guard.IsNotNull(assignedValue); 19 | Guard.IsNotNullOrWhiteSpace(variableName); 20 | VariableNameStart = variableNameStart; 21 | VariableNameEnd = variableNameEnd; 22 | AssignedValue = assignedValue; 23 | VariableName = variableName; 24 | } 25 | 26 | public override string ToString() 27 | { 28 | return $"Variable $({VariableName}) = {AssignedValue}"; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/AbstractSyntaxTree/VariableReferenceExpression.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | public sealed class VariableReferenceExpression : ReferenceExpression 4 | { 5 | public string VariableName { get; } 6 | 7 | public LinkedToken VariableToken { get; } 8 | 9 | public VariableReferenceExpression(LinkedToken token) 10 | : base(token, token) 11 | { 12 | Guard.IsNotNull(token); 13 | VariableToken = token; 14 | VariableName = token.Token.GetText(); 15 | } 16 | 17 | public override string ToString() 18 | { 19 | return $"$({VariableName})"; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/Core/CultureHelper.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | public static class CultureHelper 4 | { 5 | /// 6 | /// Determines whether the given is compatible with the . 7 | /// 8 | public static bool IsCultureApplicable(string culture, string targetCulture) 9 | { 10 | Guard.IsNotNull(culture); 11 | Guard.IsNotNull(targetCulture); 12 | return culture == SupportedCultures.Any || string.Equals(culture, targetCulture, StringComparison.OrdinalIgnoreCase); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/Core/DictionaryExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | public static class DictionaryExtensions 4 | { 5 | /// 6 | /// Gets the value at the given key, or a default value. 7 | /// 8 | public static TValue? GetValueOrDefault(this IDictionary dictionary, TKey key) 9 | { 10 | return dictionary.TryGetValue(key, out TValue? value) ? value : default; 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/Core/DictionaryWithSpecialEnumValueConverter.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using Newtonsoft.Json; 3 | 4 | namespace NotepadBasedCalculator.Api 5 | { 6 | public class DictionaryWithSpecialEnumValueConverter : JsonConverter where T : struct, Enum 7 | { 8 | public override bool CanWrite => false; 9 | 10 | public override bool CanConvert(Type objectType) 11 | { 12 | return true; 13 | } 14 | 15 | public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) 16 | { 17 | if (reader.TokenType == JsonToken.Null) 18 | { 19 | return null; 20 | } 21 | 22 | Type valueType = objectType.GetGenericArguments()[1]; 23 | Type intermediateDictionaryType = typeof(Dictionary<,>).MakeGenericType(typeof(string), valueType); 24 | 25 | var intermediateDictionary = Activator.CreateInstance(intermediateDictionaryType) as IDictionary; 26 | var finalDictionary = Activator.CreateInstance(objectType) as IDictionary; 27 | 28 | if (intermediateDictionary is not null && finalDictionary is not null) 29 | { 30 | serializer.Populate(reader, intermediateDictionary); 31 | foreach (DictionaryEntry pair in intermediateDictionary) 32 | { 33 | string? key = pair.Key.ToString(); 34 | string? value = pair.Value?.ToString(); 35 | if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(value)) 36 | { 37 | finalDictionary.Add(key, value!.ToEnum()); 38 | } 39 | } 40 | } 41 | 42 | return finalDictionary; 43 | } 44 | 45 | public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) 46 | { 47 | throw new NotSupportedException(); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/Core/EnumExtension.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.Serialization; 3 | 4 | namespace NotepadBasedCalculator.Api 5 | { 6 | /// 7 | /// Provides a set of extension for enumerations 8 | /// 9 | internal static class EnumExtension 10 | { 11 | /// 12 | /// Retrieves the 's value. 13 | /// 14 | /// The targeted enumeration 15 | /// The value 16 | /// A string that corresponds to the description of the enumeration. 17 | internal static string GetDescription(this T enumerationValue) where T : struct, Enum 18 | { 19 | Guard.IsNotNull(enumerationValue!); 20 | 21 | MemberInfo[]? memberInfo = enumerationValue.GetType().GetMember(enumerationValue.ToString()!); 22 | 23 | if (memberInfo is not null && memberInfo.Length > 0) 24 | { 25 | object[] attrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false).ToArray(); 26 | 27 | if (attrs is not null && attrs.Length > 0) 28 | { 29 | return ((DescriptionAttribute)attrs[0]).Description; 30 | } 31 | } 32 | 33 | return enumerationValue.ToString()!; 34 | } 35 | 36 | /// 37 | /// Converts the given text into an enum. 38 | /// 39 | internal static T ToEnum(this string input) where T : struct, Enum 40 | { 41 | Type enumType = typeof(T); 42 | 43 | if (Enum.TryParse(input, out T result)) 44 | { 45 | return result; 46 | } 47 | 48 | foreach (string name in Enum.GetNames(enumType)) 49 | { 50 | var fieldInfo = enumType.GetField(name)?.GetCustomAttributes(typeof(EnumMemberAttribute), true) as EnumMemberAttribute[]; 51 | if (fieldInfo is not null && fieldInfo.Length == 1) 52 | { 53 | if (fieldInfo[0].Value == input) 54 | { 55 | return (T)Enum.Parse(enumType, name); 56 | } 57 | } 58 | } 59 | 60 | return default; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/Core/Threading/AsyncLazy.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | public sealed class AsyncLazy 4 | { 5 | private readonly Lazy> _innerLazy; 6 | 7 | public bool IsValueCreated => _innerLazy.IsValueCreated && _innerLazy.Value.IsCompleted && !_innerLazy.Value.IsFaulted; 8 | 9 | public AsyncLazy(Func> valueFactory) 10 | { 11 | _innerLazy = new Lazy>(valueFactory); 12 | } 13 | 14 | public AsyncLazy(Func> valueFactory, bool isThreadSafe) 15 | { 16 | _innerLazy = new Lazy>(valueFactory, isThreadSafe); 17 | } 18 | 19 | public AsyncLazy(Func> valueFactory, LazyThreadSafetyMode mode) 20 | { 21 | _innerLazy = new Lazy>(valueFactory, mode); 22 | } 23 | 24 | public Task GetValueAsync() 25 | { 26 | return IsValueCreated ? Task.FromResult(_innerLazy.Value.Result) : _innerLazy.Value; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/Core/Threading/CancellationTokenExtension.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | public static class CancellationTokenExtension 4 | { 5 | /// 6 | /// Converts the to a 7 | /// that canceled when the is being canceled. 8 | /// 9 | public static Task AsTask(this CancellationToken cancellationToken) 10 | { 11 | var tcs = new TaskCompletionSource(); 12 | cancellationToken.Register(() => tcs.TrySetCanceled(cancellationToken), useSynchronizationContext: false); 13 | return tcs.Task; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/Core/Threading/DisposableSempahore.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | /// 4 | /// Represents a semaphore that free other threads when disposing the result of the method.. 5 | /// 6 | [DebuggerDisplay($"IsBusy = {{{nameof(IsBusy)}}}, Disposed = {{{nameof(Disposed)}}}")] 7 | public sealed class DisposableSempahore : IDisposable 8 | { 9 | private readonly object _lockObject = new(); 10 | private readonly SemaphoreSlim _semaphore; 11 | 12 | public bool Disposed { get; private set; } 13 | 14 | public bool IsBusy => _semaphore.CurrentCount == 0; 15 | 16 | public DisposableSempahore(int maxTasksCount = 1) 17 | { 18 | _semaphore = new SemaphoreSlim(maxTasksCount, maxTasksCount); 19 | } 20 | 21 | public void Dispose() 22 | { 23 | lock (_lockObject) 24 | { 25 | if (!Disposed) 26 | { 27 | Disposed = true; 28 | _semaphore.Dispose(); 29 | } 30 | } 31 | } 32 | 33 | public async Task WaitAsync(CancellationToken cancellationToken) 34 | { 35 | await _semaphore.WaitAsync(cancellationToken).ConfigureAwait(false); 36 | 37 | return new DummyDisposable(_semaphore); 38 | } 39 | 40 | private sealed class DummyDisposable : IDisposable 41 | { 42 | private readonly SemaphoreSlim _semaphore; 43 | 44 | internal DummyDisposable(SemaphoreSlim semaphore) 45 | { 46 | _semaphore = semaphore; 47 | } 48 | 49 | public void Dispose() 50 | { 51 | _semaphore.Release(); 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/Core/Threading/TaskExtension.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | /// 4 | /// Provides a set of helper method to play around with threads. 5 | /// 6 | public static class TaskExtension 7 | { 8 | /// 9 | /// Runs a task without waiting for its result. 10 | /// 11 | public static void Forget(this Task _) 12 | { 13 | } 14 | 15 | /// 16 | /// Runs a task without waiting for its result. 17 | /// 18 | public static void Forget(this Task _) 19 | { 20 | } 21 | 22 | /// 23 | /// Runs a task without waiting for its result. Swallows or handle any exception caused by the task. 24 | /// 25 | /// The action to run when an exception is caught. 26 | public static async void ForgetSafely(this Task task, Action? errorHandler = null) 27 | { 28 | try 29 | { 30 | await task.ConfigureAwait(true); 31 | } 32 | catch (Exception ex) 33 | { 34 | errorHandler?.Invoke(ex); 35 | } 36 | } 37 | 38 | /// 39 | /// Gets the result of the task synchronously, on the current thread. 40 | /// 41 | public static T CompleteOnCurrentThread(this Task task) 42 | { 43 | return task.GetAwaiter().GetResult(); 44 | } 45 | 46 | /// 47 | /// Gets an awaiter that schedules continuations on the specified scheduler. 48 | /// 49 | /// The task scheduler used to execute continuations. 50 | /// An awaitable. 51 | public static TaskSchedulerAwaiter GetAwaiter(this TaskScheduler scheduler) 52 | { 53 | Guard.IsNotNull(scheduler); 54 | return new TaskSchedulerAwaiter(scheduler); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/Data/Data.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | public abstract record Data : Token, IData 4 | { 5 | public string? Subtype { get; } 6 | 7 | public T Value { get; } 8 | 9 | public virtual int ConflictResolutionPriority => 0; 10 | 11 | public abstract string GetDisplayText(string culture); 12 | 13 | protected Data( 14 | string lineTextIncludingLineBreak, 15 | int startInLine, 16 | int endInLine, 17 | T value, 18 | string dataType, 19 | string subtype) 20 | : base(lineTextIncludingLineBreak, startInLine, endInLine, dataType) 21 | { 22 | Guard.IsNotNullOrWhiteSpace(subtype); 23 | Subtype = subtype; 24 | Value = value; 25 | } 26 | 27 | public bool IsOfSubtype(string expectedSubtype) 28 | { 29 | return string.Equals(Subtype, expectedSubtype, StringComparison.OrdinalIgnoreCase); 30 | } 31 | 32 | public abstract IData MergeDataLocations(IData otherData); 33 | 34 | public bool Equals(IData? other) 35 | { 36 | if (object.ReferenceEquals(this, other)) 37 | { 38 | return true; 39 | } 40 | 41 | if (other is not null 42 | && StartInLine == other.StartInLine 43 | && Length == other.Length) 44 | { 45 | return true; 46 | } 47 | 48 | return false; 49 | } 50 | 51 | public int CompareTo(IData? other) 52 | { 53 | return StartInLine.CompareTo(other?.StartInLine ?? 0); 54 | } 55 | 56 | public override string ToString() 57 | { 58 | return base.ToString(); 59 | } 60 | 61 | protected void ThrowIncompatibleUnitsException() 62 | { 63 | throw new IncompatibleUnitsException(); 64 | } 65 | 66 | protected void ThrowUnsupportedArithmeticOperationException() 67 | { 68 | throw new UnsupportedArithmeticOperationException(); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/Data/Definition/CurrencyValue.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | public struct CurrencyValue 4 | { 5 | public CurrencyValue(double value, string currency, string isoCurrency) 6 | { 7 | Value = value; 8 | Currency = currency; 9 | IsoCurrency = isoCurrency; 10 | } 11 | 12 | public double Value { get; } 13 | 14 | public string Currency { get; } 15 | 16 | public string IsoCurrency { get; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/Data/IData.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | /// 4 | /// Represents a data. 5 | /// 6 | public interface IData : IToken, IEquatable, IComparable 7 | { 8 | /// 9 | /// When multiple data or various type are discovered the text document at a same location, the parser 10 | /// needs to solve the conflict. This property indicates the priority to of a data over others. 11 | /// is the highest priority. 12 | /// 13 | int ConflictResolutionPriority { get; } 14 | 15 | /// 16 | /// Gets an optional internal non-localized name that represents the subtype of token. 17 | /// 18 | string? Subtype { get; } 19 | 20 | /// 21 | /// Gets a string representation of the data that will be displayed to the user. 22 | /// 23 | string GetDisplayText(string culture); 24 | 25 | /// 26 | /// Gets whether the data has the . 27 | /// 28 | bool IsOfSubtype(string expectedSubtype); 29 | 30 | /// 31 | /// Merged the locations of the current data with the given . 32 | /// 33 | IData MergeDataLocations(IData otherData); 34 | } 35 | 36 | /// 37 | /// Represents a data with a strongly typed value. 38 | /// 39 | public interface IData : IData 40 | { 41 | /// 42 | /// Gets the value of the data. 43 | /// 44 | T Value { get; } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/Data/IDataParser.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | /// 4 | /// Provides a way to extract a data out from a line of text such as a number or a date. 5 | /// 6 | public interface IDataParser 7 | { 8 | /// 9 | /// Parses the data in the given . 10 | /// 11 | IReadOnlyList? Parse(string culture, TokenizedTextLine tokenizedTextLine, CancellationToken cancellationToken); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/Data/IDecimal.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | /// 4 | /// Indicates whether the current data's value is a generic number without unit. 5 | /// 6 | public interface IDecimal : INumericData 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/Data/INumericData.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | /// 4 | /// Indicates that the data is a numeric value support arithmetic operation. 5 | /// 6 | public interface INumericData : IData 7 | { 8 | /// 9 | /// Gets whether the data's value is negative. 10 | /// 11 | bool IsNegative { get; } 12 | 13 | /// 14 | /// Gets the data's value in its current unit of measure. 15 | /// 16 | double NumericValueInCurrentUnit { get; } 17 | 18 | /// 19 | /// Gets the data's value in a standard unit of measure. 20 | /// 21 | double NumericValueInStandardUnit { get; } 22 | 23 | /// 24 | /// Creates a data from a value in the standard unit of measure. 25 | /// The returned value should usually be converted to the current unit. 26 | /// 27 | INumericData CreateFromStandardUnit(double valueInStandardUnit); 28 | 29 | /// 30 | /// Creates a data from a value in the current unit of measure. 31 | /// 32 | INumericData CreateFromCurrentUnit(double valueInCurrentUnit); 33 | 34 | /// 35 | /// Performs an addition. 36 | /// 37 | INumericData Add(INumericData otherData); 38 | 39 | /// 40 | /// Performs a substraction. 41 | /// 42 | INumericData Substract(INumericData otherData); 43 | 44 | /// 45 | /// Performs a multiplication. 46 | /// 47 | INumericData Multiply(INumericData otherData); 48 | 49 | /// 50 | /// Performs a division. 51 | /// 52 | INumericData Divide(INumericData otherData); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/Data/ISupportMultipleDataTypeForArithmeticOperation.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | /// 4 | /// Indicates that the data can perform arithmeric operation with several type of data (not only the current one) 5 | /// and will handle incompatibility itself. 6 | /// 7 | public interface ISupportMultipleDataTypeForArithmeticOperation : INumericData 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/Data/IValueRelativeToOtherData.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | /// 4 | /// Indicates that a data is always relative to another one. 5 | /// 6 | public interface IValueRelativeToOtherData : INumericData 7 | { 8 | /// 9 | /// Gets the value, in standard unit, relative to the given data. 10 | /// 11 | double GetStandardUnitValueRelativeTo(INumericData other); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/DataOperationException.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | public abstract class DataOperationException : Exception 4 | { 5 | public virtual string GetLocalizedMessage(string culture) 6 | { 7 | // TODO: Localize. 8 | return "Unknown error"; 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/Grammar/FunctionDefinition.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | [DebuggerDisplay($"FunctionFullName = {{{nameof(FunctionFullName)}}}")] 4 | public sealed class FunctionDefinition 5 | { 6 | private readonly Lazy _tokenCount; 7 | 8 | public string FunctionFullName { get; } 9 | 10 | public LinkedToken TokenizedFunctionDefinition { get; } 11 | 12 | public int TokenCount => _tokenCount.Value; 13 | 14 | public FunctionDefinition(string functionFullName, LinkedToken tokenizedFunctionDefinition) 15 | { 16 | Guard.IsNotNullOrWhiteSpace(functionFullName); 17 | Guard.IsNotNull(tokenizedFunctionDefinition); 18 | FunctionFullName = functionFullName; 19 | TokenizedFunctionDefinition = tokenizedFunctionDefinition; 20 | 21 | _tokenCount = new Lazy(() => 22 | { 23 | int count = 0; 24 | LinkedToken? token = TokenizedFunctionDefinition; 25 | while (token is not null) 26 | { 27 | count++; 28 | token = token.Next; 29 | } 30 | return count; 31 | }); 32 | } 33 | 34 | public override string ToString() 35 | { 36 | return FunctionFullName; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/Grammar/IFunctionDefinitionProvider.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | /// 4 | /// Represents a function definition provider. 5 | /// 6 | public interface IFunctionDefinitionProvider 7 | { 8 | /// 9 | /// Loads the function definitions for the given culture. 10 | /// 11 | IReadOnlyList>> LoadFunctionDefinitions(string culture); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/Grammar/IGrammarProvider.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | /// 4 | /// Represents a grammar provider. 5 | /// 6 | public interface IGrammarProvider 7 | { 8 | /// 9 | /// Loads the token definition grammars for the given culture. 10 | /// 11 | IReadOnlyList? LoadTokenDefinitionGrammars(string culture); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/Grammar/TokenDefinitionGrammar.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | using Newtonsoft.Json; 3 | 4 | namespace NotepadBasedCalculator.Api 5 | { 6 | [DataContract] 7 | public class TokenDefinitionGrammar 8 | { 9 | [DataMember(Name = "common_tokens")] 10 | public Dictionary? CommonTokens { get; set; } 11 | 12 | public static TokenDefinitionGrammar? Load(string json) 13 | { 14 | return JsonConvert.DeserializeObject(json); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/IArithmeticAndRelationOperationService.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | /// 4 | /// A service for performing binary and algebra operation on data. 5 | /// 6 | public interface IArithmeticAndRelationOperationService 7 | { 8 | /// 9 | /// Perform operation on two data. 10 | /// 11 | IData? PerformOperation(IData? leftData, BinaryOperatorType binaryOperatorType, IData? rightData); 12 | 13 | /// 14 | /// Perform binary operation on two data. 15 | /// 16 | IData? PerformBinaryOperation(IData? leftData, BinaryOperatorType binaryOperatorType, IData? rightData); 17 | 18 | /// 19 | /// Perform algebra operation on two data. 20 | /// 21 | /// Addition, Substraction, Multiplication or Division 22 | IData? PerformAlgebraOperation(IData? leftData, BinaryOperatorType binaryOperatorType, IData? rightData); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/ICurrencyService.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | /// 4 | /// Provides a way to convert currencies using recent exchange rates. 5 | /// 6 | public interface ICurrencyService 7 | { 8 | /// 9 | /// Converts a currency. 10 | /// 11 | /// i.e USD 12 | /// The value in currency. 13 | /// i.e EUR 14 | /// Returns null if or can't be found. 15 | Task ConvertCurrencyAsync(string fromCurrencyIso, double value, string toCurrencyIso); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/ILogger.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | /// 4 | /// Provides a way to log information for debugging purpose. 5 | /// 6 | public interface ILogger 7 | { 8 | void Log(string logName, params ValueTuple[]? properties); 9 | 10 | void Log(string logName, string? description, params ValueTuple[]? properties); 11 | 12 | void LogFault(string logName, Exception ex, params ValueTuple[]? properties); 13 | 14 | void LogFault(string logName, Exception ex, string description, params ValueTuple[]? properties); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/IMefProvider.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | /// 4 | /// Provides a way to import MEF components on the fly. 5 | /// 6 | public interface IMefProvider 7 | { 8 | /// 9 | /// Imports the given type. 10 | /// 11 | TExport Import(); 12 | 13 | /// 14 | /// Imports the given type. 15 | /// 16 | IEnumerable ImportMany(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/IncompatibleUnitsException.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | /// 4 | /// Exception thrown when data have incompatible units during an arithmetic operation. 5 | /// 6 | public class IncompatibleUnitsException : DataOperationException 7 | { 8 | public override string GetLocalizedMessage(string culture) 9 | { 10 | // TODO: Localize. 11 | return "Incompatible units"; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/Lexer/ILexer.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | /// 4 | /// A service for tokenizing a text. 5 | /// 6 | public interface ILexer 7 | { 8 | /// 9 | /// Tokenize the given text, line by line. 10 | /// 11 | /// See . 12 | /// The text to tokenize. 13 | IReadOnlyList Tokenize(string culture, string? wholeDocument); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/Lexer/ITokenEnumerator.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | internal interface ITokenEnumerator : IEnumerator 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/Lexer/LinkedToken.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | /// 4 | /// Represents a token and expose the access to the previous and next token. 5 | /// 6 | [DebuggerDisplay($"Token = {{{nameof(Token)}.{nameof(IToken.GetText)}()}}")] 7 | public sealed class LinkedToken 8 | { 9 | private readonly Lazy _nextToken; 10 | 11 | /// 12 | /// Gets the token before the current one. 13 | /// 14 | public LinkedToken? Previous { get; } 15 | 16 | /// 17 | /// Gets the token after the current one. 18 | /// 19 | public LinkedToken? Next => _nextToken.Value; 20 | 21 | /// 22 | /// Gets the current token information. 23 | /// 24 | public IToken Token { get; } 25 | 26 | internal LinkedToken(LinkedToken? previous, IToken token, ITokenEnumerator tokenEnumerator) 27 | { 28 | Guard.IsNotNull(token); 29 | Guard.IsNotNull(tokenEnumerator); 30 | 31 | Previous = previous; 32 | Token = token; 33 | 34 | _nextToken 35 | = new Lazy(() => 36 | { 37 | if (tokenEnumerator.MoveNext()) 38 | { 39 | Guard.IsNotNull(tokenEnumerator.Current); 40 | return new LinkedToken( 41 | previous: this, 42 | token: tokenEnumerator.Current, 43 | tokenEnumerator); 44 | } 45 | else if (tokenEnumerator.Current is not null) 46 | { 47 | return new LinkedToken( 48 | previous: this, 49 | token: tokenEnumerator.Current); 50 | } 51 | 52 | return null; 53 | }); 54 | } 55 | 56 | private LinkedToken(LinkedToken? previous, IToken token) 57 | { 58 | Guard.IsNotNull(token); 59 | Previous = previous; 60 | Token = token; 61 | _nextToken = new Lazy(() => null); 62 | } 63 | 64 | public override string? ToString() 65 | { 66 | return Token.ToString(); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/Lexer/TokenizedTextLine.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | /// 4 | /// Represents a tokenized line in a text document. 5 | /// 6 | [DebuggerDisplay($"LineNumber = {{{nameof(LineNumber)}}}, LengthIncludingLineBreak = {{{nameof(LengthIncludingLineBreak)}}}")] 7 | public sealed class TokenizedTextLine 8 | { 9 | /// 10 | /// Gets the position at which the line starts. 11 | /// 12 | public int Start { get; } 13 | 14 | /// 15 | /// Gets the position at which the line ends, excluding the line break. 16 | /// 17 | public int End => Start + Length; 18 | 19 | /// 20 | /// Gets the position at which the line ends, including the line break. 21 | /// 22 | public int EndIncludingLineBreak => Start + LengthIncludingLineBreak; 23 | 24 | /// 25 | /// Gets the length of the line break. 26 | /// 27 | public int LineBreakLength { get; } 28 | 29 | /// 30 | /// Gets the length of the line, excluding the line break. 31 | /// 32 | public int Length => LengthIncludingLineBreak - LineBreakLength; 33 | 34 | /// 35 | /// Gets the length of the line, including the line break. 36 | /// 37 | public int LengthIncludingLineBreak { get; } 38 | 39 | /// 40 | /// Gets the line number in the text document. 41 | /// 42 | public int LineNumber { get; } 43 | 44 | /// 45 | /// Gets a representation of the line, including the line break. 46 | /// 47 | public string LineTextIncludingLineBreak { get; } 48 | 49 | /// 50 | /// Gets the list of tokens in the line. 51 | /// 52 | public LinkedToken? Tokens { get; } 53 | 54 | internal TokenizedTextLine( 55 | int start, 56 | int lineNumber, 57 | int lineBreakLength, 58 | string? lineTextIncludingLineBreak, 59 | LinkedToken? tokens) 60 | { 61 | Guard.IsGreaterThanOrEqualTo(start, 0); 62 | Guard.IsGreaterThanOrEqualTo(lineNumber, 0); 63 | Guard.IsGreaterThanOrEqualTo(lineBreakLength, 0); 64 | 65 | Start = start; 66 | LineNumber = lineNumber; 67 | LineBreakLength = lineBreakLength; 68 | LineTextIncludingLineBreak = lineTextIncludingLineBreak ?? string.Empty; 69 | LengthIncludingLineBreak = LineTextIncludingLineBreak.Length; 70 | Tokens = tokens; 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/Metadata/CultureAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | /// 4 | /// Defines the culture supported by a component. 5 | /// 6 | /// 7 | /// 8 | /// Set to if the component is culture invariant. 9 | /// 10 | /// 11 | [MetadataAttribute] 12 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] 13 | public class CultureAttribute : Attribute 14 | { 15 | public string CultureCode { get; } 16 | 17 | public CultureAttribute(string cultureCode) 18 | { 19 | CultureCode = cultureCode; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/Metadata/CultureCodeMetadata.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | public class CultureCodeMetadata 4 | { 5 | public string[] CultureCodes { get; set; } 6 | 7 | public CultureCodeMetadata(IDictionary metadata) 8 | { 9 | if (metadata.TryGetValue(nameof(CultureAttribute.CultureCode), out object? value) && value is not null) 10 | { 11 | if (value is string culture) 12 | { 13 | CultureCodes = new[] { culture }; 14 | } 15 | else if (value is string[] cultures) 16 | { 17 | CultureCodes = cultures; 18 | } 19 | else 20 | { 21 | ThrowHelper.ThrowInvalidDataException($"Unable to understand MEF's '{nameof(CultureAttribute.CultureCode)}' information."); 22 | } 23 | } 24 | else 25 | { 26 | CultureCodes = new[] { SupportedCultures.Any }; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/Metadata/FunctionInterpreterMetadata.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | public sealed class FunctionInterpreterMetadata : CultureCodeMetadata 4 | { 5 | public string Name { get; } 6 | 7 | public FunctionInterpreterMetadata(IDictionary metadata) 8 | : base(metadata) 9 | { 10 | if (metadata.TryGetValue(nameof(NameAttribute.Name), out object? value) && value is string name) 11 | { 12 | Name = name; 13 | } 14 | else 15 | { 16 | Name = string.Empty; 17 | } 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/Metadata/IOrderableMetadata.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | public interface IOrderableMetadata 4 | { 5 | IReadOnlyList Before { get; } 6 | 7 | IReadOnlyList After { get; } 8 | 9 | string Name { get; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/Metadata/NameAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | /// 4 | /// Defines the internal name of this component. This name can be used to explicitly request this component to be invoked. 5 | /// 6 | [MetadataAttribute] 7 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] 8 | public class NameAttribute : Attribute 9 | { 10 | public string Name { get; } 11 | 12 | public NameAttribute(string name) 13 | { 14 | Guard.IsNotEmpty(name); 15 | Name = name; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/Metadata/OrderAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | /// 4 | /// Defines the priority of this component over others. 5 | /// 6 | [MetadataAttribute] 7 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] 8 | public class OrderAttribute : Attribute 9 | { 10 | private string _before = string.Empty; 11 | private string _after = string.Empty; 12 | 13 | /// 14 | /// Gets or sets the internal name of a component to compare with. 15 | /// 16 | public string Before 17 | { 18 | get 19 | { 20 | return _before; 21 | } 22 | set 23 | { 24 | Guard.IsNotNullOrEmpty(value); 25 | _before = value; 26 | } 27 | } 28 | 29 | /// 30 | /// Gets or sets the internal name of a component to compare with. 31 | /// 32 | public string After 33 | { 34 | get 35 | { 36 | return _after; 37 | } 38 | set 39 | { 40 | Guard.IsNotNullOrEmpty(value); 41 | _after = value; 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/Metadata/ParserAndInterpreterMetadata.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | public sealed class ParserAndInterpreterMetadata : CultureCodeMetadata, IOrderableMetadata 4 | { 5 | public IReadOnlyList Before { get; } 6 | 7 | public IReadOnlyList After { get; } 8 | 9 | public string Name { get; } 10 | 11 | public ParserAndInterpreterMetadata(IDictionary metadata) 12 | : base(metadata) 13 | { 14 | Before = metadata.GetValueOrDefault(nameof(OrderAttribute.Before)) as IReadOnlyList ?? Array.Empty(); 15 | After = metadata.GetValueOrDefault(nameof(OrderAttribute.After)) as IReadOnlyList ?? Array.Empty(); 16 | Name = metadata.GetValueOrDefault(nameof(NameAttribute.Name)) as string ?? string.Empty; 17 | Guard.IsNotEmpty(Name); 18 | 19 | if (Before.Count > 0) 20 | { 21 | var before = new List(); 22 | for (int i = 0; i < Before.Count; i++) 23 | { 24 | if (!string.IsNullOrEmpty(Before[i])) 25 | { 26 | before.Add(Before[i]); 27 | } 28 | } 29 | 30 | Before = before; 31 | } 32 | 33 | if (After.Count > 0) 34 | { 35 | var after = new List(); 36 | for (int i = 0; i < After.Count; i++) 37 | { 38 | if (!string.IsNullOrEmpty(After[i])) 39 | { 40 | after.Add(After[i]); 41 | } 42 | } 43 | 44 | After = after; 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/Metadata/SupportedCultures.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | /// 4 | /// Contains a list of pre-defined supported cultures. 5 | /// 6 | public static class SupportedCultures 7 | { 8 | public const string Any = ""; 9 | public const string Arabic = "ar-*"; 10 | public const string English = "en-us"; 11 | public const string EnglishOthers = "en-*"; 12 | public const string Chinese = "zh-cn"; 13 | public const string Spanish = "es-es"; 14 | public const string SpanishMexican = "es-mx"; 15 | public const string Portuguese = "pt-br"; 16 | public const string French = "fr-fr"; 17 | public const string German = "de-de"; 18 | public const string Italian = "it-it"; 19 | public const string Japanese = "ja-jp"; 20 | public const string Dutch = "nl-nl"; 21 | public const string Korean = "ko-kr"; 22 | public const string Swedish = "sv-se"; 23 | public const string Bulgarian = "bg-bg"; 24 | public const string Turkish = "tr-tr"; 25 | public const string Hindi = "hi-in"; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/NotepadBasedCalculator.Api.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(NetCoreAndStandard) 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/ParserAndInterpreter/ExpressionParserAndInterpreterResult.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | /// 4 | /// Represents the result of a parsed and interpreted expression. 5 | /// 6 | [DebuggerDisplay($"ResultedData = {{{nameof(ResultedData)}}}, ParsedExpression = {{{nameof(ParsedExpression)}}}, Error = {{{nameof(Error)}}}")] 7 | public record ExpressionParserAndInterpreterResult 8 | { 9 | /// 10 | /// Gets or sets the resulting data out of the interpretation. 11 | /// 12 | public IData? ResultedData { get; set; } = null; 13 | 14 | /// 15 | /// Gets or sets the parsed expression. 16 | /// 17 | public Expression? ParsedExpression { get; set; } = null; 18 | 19 | /// 20 | /// Gets or sets the token the next parser should continue with. 21 | /// 22 | public LinkedToken? NextTokenToContinueWith { get; set; } = null; 23 | 24 | /// 25 | /// Gets or sets an exception that may have been triggered. 26 | /// 27 | /// 28 | /// When this property is set to a non-null value, the interpretation will stop, meaning that other parsers and 29 | /// interpreter won't have a chance to parse. Set this property only to indicate that the parsing should stop. 30 | /// 31 | public DataOperationException? Error { get; set; } = null; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/ParserAndInterpreter/IExpressionParserAndInterpreter.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | /// 4 | /// Provides a parser and interpreter for an expression. 5 | /// 6 | /// 7 | /// 8 | /// [Export(typeof(IExpressionParserAndInterpreter))] 9 | /// [Culture(SupportedCultures.Any)] 10 | /// [Name("MyExpression")] 11 | /// public class MyStatementParserAndInterpreter : IExpressionParserAndInterpreter 12 | /// { 13 | /// [Import] 14 | /// IParserAndInterpreterService ParserAndInterpreterService { get; } 15 | /// } 16 | /// 17 | /// 18 | public interface IExpressionParserAndInterpreter 19 | { 20 | /// 21 | /// Gets a service helping with parsing inner expressions, statement and perform arithmetic operation. 22 | /// 23 | IParserAndInterpreterService ParserAndInterpreterService { get; } 24 | 25 | /// 26 | /// Try to parse an expression at the in the document. 27 | /// 28 | /// See . 29 | /// The current token being read in the tokenized line. 30 | /// A service for managing variables. 31 | /// A value that can be used to report the parsed statement, interpreted result or error. 32 | /// A token that cancels when needed. 33 | /// Returns true if the parser and interpreter produced a without issue. Otherwise, false. 34 | /// An exception of type can be thrown to indicate an error. 35 | Task TryParseAndInterpretExpressionAsync( 36 | string culture, 37 | LinkedToken currentToken, 38 | IVariableService variableService, 39 | ExpressionParserAndInterpreterResult result, 40 | CancellationToken cancellationToken); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/ParserAndInterpreter/IFunctionInterpreter.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | /// 4 | /// Provides an interpreter for a function. 5 | /// 6 | /// 7 | /// 8 | /// [Export(typeof(IFunctionInterpreter))] 9 | /// [Name("general.myFunction")] 10 | /// [Culture(SupportedCultures.English)] 11 | /// public class MyFunctionInterpreter : IFunctionInterpreter 12 | /// { 13 | /// } 14 | /// 15 | /// 16 | /// 17 | /// The function should be defined in a grammar file and at least one 18 | /// should be exported. 19 | /// 20 | public interface IFunctionInterpreter 21 | { 22 | /// 23 | /// Interprets the function. 24 | /// 25 | /// See 26 | /// The definition of the function being interpreted. 27 | /// The detected data that go as parameters of the function 28 | /// A token that cancels when needed. 29 | /// The result of the interpretation. Returns null if no result has been found. 30 | /// An exception of type can be thrown to indicate an error. 31 | Task InterpretFunctionAsync( 32 | string culture, 33 | FunctionDefinition functionDefinition, 34 | IReadOnlyList detectedData, 35 | CancellationToken cancellationToken); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/ParserAndInterpreter/IParserAndInterpretersRepository.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | /// 4 | /// A service that helps accessing parsers and interpreters. 5 | /// 6 | internal interface IParserAndInterpretersRepository 7 | { 8 | /// 9 | /// Gets all the data parsers that apply to the given . 10 | /// 11 | /// See 12 | /// An ordered list of item by priority. 13 | IEnumerable GetApplicableDataParsers(string culture); 14 | 15 | /// 16 | /// Gets all the statement parsers and interpreters that apply to the given . 17 | /// 18 | /// See 19 | /// An ordered list of item by priority. 20 | IEnumerable GetApplicableStatementParsersAndInterpreters(string culture); 21 | 22 | /// 23 | /// Gets all the expression parsers and interpreters that apply to the given . 24 | /// 25 | /// See 26 | /// An ordered list of item by priority. 27 | IEnumerable GetApplicableExpressionParsersAndInterpreters(string culture); 28 | 29 | /// 30 | /// Gets a specific list of expression parsers and interpreters. 31 | /// 32 | /// See 33 | /// Unordered list of names of expression parser and interpreter to retrieve 34 | /// An ordered list of item by priority. 35 | IExpressionParserAndInterpreter[] GetExpressionParserAndInterpreters(string culture, params string[] expressionParserAndInterpreterNames); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/ParserAndInterpreter/IStatementParserAndInterpreter.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | /// 4 | /// Provides a parser and interpreter for a statement. 5 | /// 6 | /// 7 | /// 8 | /// [Export(typeof(IStatementParserAndInterpreter))] 9 | /// [Culture(SupportedCultures.Any)] 10 | /// [Name("MyStatement")] 11 | /// public class MyStatementParserAndInterpreter : IStatementParserAndInterpreter 12 | /// { 13 | /// [Import] 14 | /// IParserAndInterpreterService ParserAndInterpreterService { get; } 15 | /// } 16 | /// 17 | /// 18 | public interface IStatementParserAndInterpreter 19 | { 20 | /// 21 | /// Gets a service helping with parsing inner expressions, statement and perform arithmetic operation. 22 | /// 23 | IParserAndInterpreterService ParserAndInterpreterService { get; } 24 | 25 | /// 26 | /// Try to parse a statement at the in the document. 27 | /// 28 | /// See . 29 | /// The current token being read in the tokenized line. 30 | /// A service for managing variables. 31 | /// A value that can be used to report the parsed statement, interpreted result or error. 32 | /// A token that cancels when needed. 33 | /// Returns true if the parser and interpreter produced a without issue. Otherwise, false. 34 | /// An exception of type can be thrown to indicate an error. 35 | Task TryParseAndInterpretStatementAsync( 36 | string culture, 37 | LinkedToken currentToken, 38 | IVariableService variableService, 39 | StatementParserAndInterpreterResult result, 40 | CancellationToken cancellationToken); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/ParserAndInterpreter/IVariableService.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | /// 4 | /// A service for managing variables. 5 | /// 6 | public interface IVariableService 7 | { 8 | /// 9 | /// Sets the of the given . 10 | /// If the variable doesn't exist yet, it will be created. 11 | /// is case sensitive. 12 | /// 13 | /// Case sensitive variable name. 14 | /// The value of the variable. 15 | void SetVariableValue(string variableName, IData? value); 16 | 17 | /// 18 | /// Gets the value of the given . 19 | /// 20 | /// Case sensitive variable name. 21 | /// Returns null if the variable doesn't exist. 22 | IData? GetVariableValue(string variableName); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/ParserAndInterpreter/PredefinedExpressionParserAndInterpreterNames.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | /// 4 | /// Contains a list of pre-defined known expression parser and interpreter names. 5 | /// 6 | public static class PredefinedExpressionParserAndInterpreterNames 7 | { 8 | public const string NumericalExpression = nameof(NumericalExpression); 9 | 10 | public const string PrimitiveExpression = nameof(PrimitiveExpression); 11 | 12 | public const string ConditionalExpression = nameof(ConditionalExpression); 13 | 14 | public const string FunctionExpression = nameof(FunctionExpression); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/ParserAndInterpreter/PredefinedStatementParserAndInterpreterNames.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | /// 4 | /// Contains a list of pre-defined known statement parser and interpreter names. 5 | /// 6 | public static class PredefinedStatementParserAndInterpreterNames 7 | { 8 | public const string CommentStatement = nameof(CommentStatement); 9 | 10 | public const string HeaderStatement = nameof(HeaderStatement); 11 | 12 | public const string ConditionStatement = nameof(ConditionStatement); 13 | 14 | public const string ConditionExpressionStatement = nameof(ConditionExpressionStatement); 15 | 16 | public const string NumericalExpressionStatement = nameof(NumericalExpressionStatement); 17 | 18 | public const string FunctionExpressionStatement = nameof(FunctionExpressionStatement); 19 | 20 | public const string VariableStatement = nameof(VariableStatement); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/ParserAndInterpreter/StatementParserAndInterpreterResult.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | /// 4 | /// Represents the result of a parsed and interpreted statement. 5 | /// 6 | [DebuggerDisplay($"ParsedStatement = {{{nameof(ParsedStatement)}}}, Error = {{{nameof(Error)}}}, ResultedData = [{{{nameof(ResultedData)}}}]")] 7 | public record StatementParserAndInterpreterResult 8 | { 9 | /// 10 | /// Gets or sets the resulting data out of the interpretation. 11 | /// 12 | public IData? ResultedData { get; set; } = null; 13 | 14 | /// 15 | /// Gets or sets the parsed statement. 16 | /// 17 | public Statement? ParsedStatement { get; set; } = null; 18 | 19 | /// 20 | /// Gets or sets an exception that may have been triggered. 21 | /// 22 | /// 23 | /// When this property is set to a non-null value, the interpretation will stop, meaning that other parsers and 24 | /// interpreter won't have a chance to parse. Set this property only to indicate that the parsing should stop. 25 | /// 26 | public DataOperationException? Error { get; set; } = null; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/UnsupportedArithmeticOperationException.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | /// 4 | /// Exception thrown when an arithmetic operation isn't supported. 5 | /// 6 | public class UnsupportedArithmeticOperationException : DataOperationException 7 | { 8 | public override string GetLocalizedMessage(string culture) 9 | { 10 | // TODO: Localize. 11 | return "Unsupported arithmetic operation"; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.BuiltInPlugins/Data/BooleanDataParser.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Recognizers.Text; 2 | 3 | namespace NotepadBasedCalculator.BuiltInPlugins.Data 4 | { 5 | [Export(typeof(IDataParser))] 6 | [Culture(SupportedCultures.Any)] 7 | public sealed class BooleanDataParser : IDataParser 8 | { 9 | public IReadOnlyList? Parse(string culture, TokenizedTextLine tokenizedTextLine, CancellationToken cancellationToken) 10 | { 11 | var results = new List(); 12 | LinkedToken? currentToken = tokenizedTextLine.Tokens; 13 | while (currentToken is not null) 14 | { 15 | if (currentToken.Token.IsOfType(PredefinedTokenAndDataTypeNames.TrueIdentifier)) 16 | { 17 | results.Add( 18 | new BooleanData( 19 | tokenizedTextLine.LineTextIncludingLineBreak, 20 | currentToken.Token.StartInLine, 21 | currentToken.Token.EndInLine, 22 | true)); 23 | } 24 | else if (currentToken.Token.IsOfType(PredefinedTokenAndDataTypeNames.FalseIdentifier)) 25 | { 26 | results.Add( 27 | new BooleanData( 28 | tokenizedTextLine.LineTextIncludingLineBreak, 29 | currentToken.Token.StartInLine, 30 | currentToken.Token.EndInLine, 31 | false)); 32 | } 33 | 34 | currentToken = currentToken.Next; 35 | } 36 | 37 | return results; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.BuiltInPlugins/Data/NumberDataParser.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Recognizers.Text; 2 | using Microsoft.Recognizers.Text.Number; 3 | using Constants = Microsoft.Recognizers.Text.Number.Constants; 4 | 5 | namespace NotepadBasedCalculator.BuiltInPlugins.Data 6 | { 7 | [Export(typeof(IDataParser))] 8 | [Culture(SupportedCultures.Any)] 9 | public sealed class NumberDataParser : IDataParser 10 | { 11 | private const string Subtype = "subtype"; 12 | private const string Value = "value"; 13 | 14 | public IReadOnlyList? Parse(string culture, TokenizedTextLine tokenizedTextLine, CancellationToken cancellationToken) 15 | { 16 | List modelResults = NumberRecognizer.RecognizeNumber(tokenizedTextLine.LineTextIncludingLineBreak, culture); 17 | cancellationToken.ThrowIfCancellationRequested(); 18 | 19 | var data = new List(); 20 | 21 | for (int i = 0; i < modelResults.Count; i++) 22 | { 23 | ModelResult modelResult = modelResults[i]; 24 | 25 | if (modelResult.Resolution is not null) 26 | { 27 | string valueString = (string)modelResult.Resolution[Value]; 28 | switch (modelResult.Resolution[Subtype]) 29 | { 30 | case Constants.INTEGER: 31 | case Constants.DECIMAL: 32 | case Constants.POWER: 33 | data.Add( 34 | new DecimalData( 35 | tokenizedTextLine.LineTextIncludingLineBreak, 36 | modelResult.Start, 37 | modelResult.End + 1, 38 | double.Parse(valueString))); 39 | break; 40 | 41 | case Constants.FRACTION: 42 | data.Add( 43 | new FractionData( 44 | tokenizedTextLine.LineTextIncludingLineBreak, 45 | modelResult.Start, 46 | modelResult.End + 1, 47 | double.Parse(valueString))); 48 | break; 49 | 50 | default: 51 | #if DEBUG 52 | ThrowHelper.ThrowNotSupportedException(); 53 | #endif 54 | break; 55 | } 56 | } 57 | } 58 | 59 | return data; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.BuiltInPlugins/Data/OrdinalDataParser.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Recognizers.Text; 2 | using Microsoft.Recognizers.Text.Number; 3 | using Constants = Microsoft.Recognizers.Text.Number.Constants; 4 | 5 | namespace NotepadBasedCalculator.BuiltInPlugins.Data 6 | { 7 | [Export(typeof(IDataParser))] 8 | [Culture(SupportedCultures.Any)] 9 | public sealed class OrdinalDataParser : IDataParser 10 | { 11 | private const string Value = "value"; 12 | private const string NullValue = "null"; 13 | 14 | public IReadOnlyList? Parse(string culture, TokenizedTextLine tokenizedTextLine, CancellationToken cancellationToken) 15 | { 16 | List modelResults = NumberRecognizer.RecognizeOrdinal(tokenizedTextLine.LineTextIncludingLineBreak, culture); 17 | cancellationToken.ThrowIfCancellationRequested(); 18 | 19 | var data = new List(); 20 | 21 | for (int i = 0; i < modelResults.Count; i++) 22 | { 23 | ModelResult modelResult = modelResults[i]; 24 | 25 | if (modelResult.Resolution is not null) 26 | { 27 | string valueString = (string)modelResult.Resolution[Value]; 28 | switch (modelResult.TypeName) 29 | { 30 | case Constants.MODEL_ORDINAL: 31 | if (!string.Equals(NullValue, valueString, StringComparison.OrdinalIgnoreCase)) 32 | { 33 | data.Add( 34 | new OrdinalData( 35 | tokenizedTextLine.LineTextIncludingLineBreak, 36 | modelResult.Start, 37 | modelResult.End + 1, 38 | long.Parse(valueString))); 39 | } 40 | break; 41 | 42 | case Constants.MODEL_ORDINAL_RELATIVE: 43 | // TODO 44 | ThrowHelper.ThrowNotSupportedException(); 45 | break; 46 | 47 | default: 48 | #if DEBUG 49 | ThrowHelper.ThrowNotSupportedException(); 50 | #endif 51 | break; 52 | } 53 | } 54 | } 55 | 56 | return data; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.BuiltInPlugins/Data/TemperatureDataParser.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Recognizers.Text; 2 | using Microsoft.Recognizers.Text.NumberWithUnit; 3 | using NotepadBasedCalculator.BuiltInPlugins.Grammars; 4 | using UnitsNet; 5 | using UnitsNet.Units; 6 | 7 | namespace NotepadBasedCalculator.BuiltInPlugins.Data 8 | { 9 | [Export(typeof(IDataParser))] 10 | [Culture(SupportedCultures.Any)] 11 | public sealed class TemperatureDataParser : IDataParser 12 | { 13 | private const string Value = "value"; 14 | private const string Unit = "unit"; 15 | 16 | private readonly UnitMapProvider _unitMapProvider; 17 | 18 | [ImportingConstructor] 19 | public TemperatureDataParser(UnitMapProvider unitMapProvider) 20 | { 21 | _unitMapProvider = unitMapProvider; 22 | } 23 | 24 | public IReadOnlyList? Parse(string culture, TokenizedTextLine tokenizedTextLine, CancellationToken cancellationToken) 25 | { 26 | List modelResults = NumberWithUnitRecognizer.RecognizeTemperature(tokenizedTextLine.LineTextIncludingLineBreak, culture); 27 | cancellationToken.ThrowIfCancellationRequested(); 28 | 29 | var data = new List(); 30 | 31 | if (modelResults.Count > 0) 32 | { 33 | UnitMap unitMap = _unitMapProvider.LoadUnitMap(culture); 34 | 35 | for (int i = 0; i < modelResults.Count; i++) 36 | { 37 | ModelResult modelResult = modelResults[i]; 38 | 39 | if (modelResult.Resolution is not null) 40 | { 41 | string valueString = (string)modelResult.Resolution[Value]; 42 | string unit = (string)modelResult.Resolution[Unit]; 43 | 44 | if (unitMap.Temperature.TryGetValue(unit, out TemperatureUnit temperatureUnit)) 45 | { 46 | data.Add( 47 | new TemperatureData( 48 | tokenizedTextLine.LineTextIncludingLineBreak, 49 | modelResult.Start, 50 | modelResult.End + 1, 51 | new Temperature(double.Parse(valueString), temperatureUnit))); 52 | } 53 | } 54 | } 55 | } 56 | 57 | return data; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.BuiltInPlugins/Data/UnitMap.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | using Newtonsoft.Json; 3 | using UnitsNet.Units; 4 | 5 | namespace NotepadBasedCalculator.BuiltInPlugins.Data 6 | { 7 | [DataContract] 8 | internal sealed class UnitMap 9 | { 10 | [JsonConverter(typeof(DictionaryWithSpecialEnumValueConverter))] 11 | [DataMember(Name = "Length")] 12 | public Dictionary Length { get; set; } = null!; 13 | 14 | [JsonConverter(typeof(DictionaryWithSpecialEnumValueConverter))] 15 | [DataMember(Name = "Information")] 16 | public Dictionary Information { get; set; } = null!; 17 | 18 | [JsonConverter(typeof(DictionaryWithSpecialEnumValueConverter))] 19 | [DataMember(Name = "Area")] 20 | public Dictionary Area { get; set; } = null!; 21 | 22 | [JsonConverter(typeof(DictionaryWithSpecialEnumValueConverter))] 23 | [DataMember(Name = "Speed")] 24 | public Dictionary Speed { get; set; } = null!; 25 | 26 | [JsonConverter(typeof(DictionaryWithSpecialEnumValueConverter))] 27 | [DataMember(Name = "Volume")] 28 | public Dictionary Volume { get; set; } = null!; 29 | 30 | [JsonConverter(typeof(DictionaryWithSpecialEnumValueConverter))] 31 | [DataMember(Name = "Mass")] 32 | public Dictionary Mass { get; set; } = null!; 33 | 34 | [JsonConverter(typeof(DictionaryWithSpecialEnumValueConverter))] 35 | [DataMember(Name = "Angle")] 36 | public Dictionary Angle { get; set; } = null!; 37 | 38 | [JsonConverter(typeof(DictionaryWithSpecialEnumValueConverter))] 39 | [DataMember(Name = "Temperature")] 40 | public Dictionary Temperature { get; set; } = null!; 41 | 42 | public static UnitMap Load(string json) 43 | { 44 | UnitMap? result = JsonConvert.DeserializeObject(json); 45 | Guard.IsNotNull(result); 46 | return result; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.BuiltInPlugins/Functions/General/MidpointInterpreter.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.BuiltInPlugins.Functions.General 2 | { 3 | [Export(typeof(IFunctionInterpreter))] 4 | [Name("general.midpoint")] 5 | [Culture(SupportedCultures.English)] 6 | internal sealed class MidpointInterpreter : IFunctionInterpreter 7 | { 8 | public Task InterpretFunctionAsync( 9 | string culture, 10 | FunctionDefinition functionDefinition, 11 | IReadOnlyList detectedData, 12 | CancellationToken cancellationToken) 13 | { 14 | Guard.HasSizeEqualTo(detectedData, 2); 15 | detectedData[0].IsOfType("numeric"); 16 | detectedData[1].IsOfType("numeric"); 17 | var firstNumber = (INumericData)detectedData[0]; 18 | var secondNumber = (INumericData)detectedData[1]; 19 | 20 | double first = firstNumber.NumericValueInStandardUnit; 21 | double second = secondNumber.NumericValueInStandardUnit; 22 | double result = (first + second) / 2; 23 | 24 | return Task.FromResult((IData?)secondNumber.CreateFromStandardUnit(result).MergeDataLocations(firstNumber)); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.BuiltInPlugins/Functions/General/RandomNumberInterpreter.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.BuiltInPlugins.Functions.General 2 | { 3 | [Export(typeof(IFunctionInterpreter))] 4 | [Name("general.random")] 5 | [Culture(SupportedCultures.English)] 6 | internal sealed class RandomNumberInterpreter : IFunctionInterpreter 7 | { 8 | private readonly Random _random = new(); 9 | 10 | public Task InterpretFunctionAsync( 11 | string culture, 12 | FunctionDefinition functionDefinition, 13 | IReadOnlyList detectedData, 14 | CancellationToken cancellationToken) 15 | { 16 | Guard.HasSizeEqualTo(detectedData, 2); 17 | detectedData[0].IsOfType("numeric"); 18 | detectedData[1].IsOfType("numeric"); 19 | var firstNumber = (INumericData)detectedData[0]; 20 | var secondNumber = (INumericData)detectedData[1]; 21 | 22 | double result = 0; 23 | double first = firstNumber.NumericValueInStandardUnit; 24 | double second = secondNumber.NumericValueInStandardUnit; 25 | 26 | if ((first == 0 && second == 1) || (first == 1 && second == 0)) 27 | { 28 | result = _random.NextDouble(); 29 | } 30 | else if (first >= second) 31 | { 32 | result = _random.Next((int)second, (int)first); 33 | } 34 | else if (first < second) 35 | { 36 | result = _random.Next((int)first, (int)second); 37 | } 38 | 39 | return Task.FromResult((IData?)secondNumber.CreateFromStandardUnit(result).MergeDataLocations(firstNumber)); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.BuiltInPlugins/Functions/General/RemainderInterpreter.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.BuiltInPlugins.Functions.General 2 | { 3 | [Export(typeof(IFunctionInterpreter))] 4 | [Name("general.remainder")] 5 | [Culture(SupportedCultures.English)] 6 | internal sealed class RemainderInterpreter : IFunctionInterpreter 7 | { 8 | public Task InterpretFunctionAsync( 9 | string culture, 10 | FunctionDefinition functionDefinition, 11 | IReadOnlyList detectedData, 12 | CancellationToken cancellationToken) 13 | { 14 | Guard.HasSizeEqualTo(detectedData, 2); 15 | detectedData[0].IsOfType("numeric"); 16 | detectedData[1].IsOfType("numeric"); 17 | var firstNumber = (INumericData)detectedData[0]; 18 | var secondNumber = (INumericData)detectedData[1]; 19 | 20 | double first = firstNumber.NumericValueInStandardUnit; 21 | double second = secondNumber.NumericValueInStandardUnit; 22 | double result = first % second; 23 | 24 | return Task.FromResult((IData?)secondNumber.CreateFromStandardUnit(result).MergeDataLocations(firstNumber)); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.BuiltInPlugins/Functions/Percentage/IsPercentOfWhatInterpreter.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.BuiltInPlugins.Functions.Percentage 2 | { 3 | [Export(typeof(IFunctionInterpreter))] 4 | [Name("percentage.isPercentOfWhat")] 5 | [Culture(SupportedCultures.English)] 6 | internal sealed class IsPercentOfWhatInterpreter : IFunctionInterpreter 7 | { 8 | [Import] 9 | public IArithmeticAndRelationOperationService ArithmeticAndRelationOperationService { get; set; } = null!; 10 | 11 | public Task InterpretFunctionAsync( 12 | string culture, 13 | FunctionDefinition functionDefinition, 14 | IReadOnlyList detectedData, 15 | CancellationToken cancellationToken) 16 | { 17 | Guard.HasSizeEqualTo(detectedData, 2); 18 | 19 | PercentageData percentageData; 20 | INumericData numericData; 21 | 22 | if (detectedData[0].IsOfSubtype("percentage") 23 | && detectedData[1].IsOfType("numeric")) 24 | { 25 | percentageData = (PercentageData)detectedData[0]; 26 | numericData = (INumericData)detectedData[1]; 27 | } 28 | else if (detectedData[0].IsOfType("numeric") 29 | && detectedData[1].IsOfSubtype("percentage")) 30 | { 31 | numericData = (INumericData)detectedData[0]; 32 | percentageData = (PercentageData)detectedData[1]; 33 | } 34 | else 35 | { 36 | ThrowHelper.ThrowNotSupportedException(); 37 | return null; 38 | } 39 | 40 | // 250 * (1 / 0.25) 41 | // 250 * 4 42 | // = 1000 43 | // so 250 is 25% of 1000. 44 | // also, 250 is 25% off 1000. 45 | return Task.FromResult( 46 | ArithmeticAndRelationOperationService.PerformAlgebraOperation( 47 | numericData, 48 | BinaryOperatorType.Multiply, 49 | numericData.CreateFromCurrentUnit(1 / percentageData.NumericValueInCurrentUnit))); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.BuiltInPlugins/Functions/Percentage/IsPercentOnWhatInterpreter.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.BuiltInPlugins.Functions.Percentage 2 | { 3 | [Export(typeof(IFunctionInterpreter))] 4 | [Name("percentage.isPercentOnWhat")] 5 | [Culture(SupportedCultures.English)] 6 | internal sealed class IsPercentOnWhatInterpreter : IFunctionInterpreter 7 | { 8 | [Import] 9 | public IArithmeticAndRelationOperationService ArithmeticAndRelationOperationService { get; set; } = null!; 10 | 11 | public Task InterpretFunctionAsync( 12 | string culture, 13 | FunctionDefinition functionDefinition, 14 | IReadOnlyList detectedData, 15 | CancellationToken cancellationToken) 16 | { 17 | Guard.HasSizeEqualTo(detectedData, 2); 18 | 19 | PercentageData percentageData; 20 | INumericData numericData; 21 | 22 | if (detectedData[0].IsOfSubtype("percentage") 23 | && detectedData[1].IsOfType("numeric")) 24 | { 25 | percentageData = (PercentageData)detectedData[0]; 26 | numericData = (INumericData)detectedData[1]; 27 | } 28 | else if (detectedData[0].IsOfType("numeric") 29 | && detectedData[1].IsOfSubtype("percentage")) 30 | { 31 | numericData = (INumericData)detectedData[0]; 32 | percentageData = (PercentageData)detectedData[1]; 33 | } 34 | else 35 | { 36 | ThrowHelper.ThrowNotSupportedException(); 37 | return null; 38 | } 39 | 40 | // 250 / (1 + 0.25) 41 | // 250 / 125% 42 | // = 200 43 | // so 250 is 25% on 200. 44 | return Task.FromResult( 45 | ArithmeticAndRelationOperationService.PerformAlgebraOperation( 46 | numericData, 47 | BinaryOperatorType.Division, 48 | numericData.CreateFromCurrentUnit(1 + percentageData.NumericValueInCurrentUnit))); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.BuiltInPlugins/Functions/Percentage/IsWhatPercentOfInterpreter.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.BuiltInPlugins.Functions.Percentage 2 | { 3 | [Export(typeof(IFunctionInterpreter))] 4 | [Name("percentage.isWhatPercentOf")] 5 | [Culture(SupportedCultures.English)] 6 | internal sealed class IsWhatPercentOfInterpreter : IFunctionInterpreter 7 | { 8 | [Import] 9 | public IArithmeticAndRelationOperationService ArithmeticAndRelationOperationService { get; set; } = null!; 10 | 11 | public Task InterpretFunctionAsync( 12 | string culture, 13 | FunctionDefinition functionDefinition, 14 | IReadOnlyList detectedData, 15 | CancellationToken cancellationToken) 16 | { 17 | Guard.HasSizeEqualTo(detectedData, 2); 18 | detectedData[0].IsOfSubtype("numeric"); 19 | detectedData[1].IsOfType("numeric"); 20 | var firstNumericData = (INumericData)detectedData[0]; 21 | var secondNumericData = (INumericData)detectedData[1]; 22 | 23 | // ((62.5 * 100) / 250) / 100 = 0.25 24 | // So 62.5 represents 25% of 250. 25 | 26 | IData? firstNumericDataMultipliedPerOneHundred 27 | = ArithmeticAndRelationOperationService.PerformAlgebraOperation( 28 | firstNumericData, 29 | BinaryOperatorType.Multiply, 30 | new DecimalData( 31 | firstNumericData.LineTextIncludingLineBreak, 32 | firstNumericData.StartInLine, 33 | firstNumericData.EndInLine, 34 | 100)); 35 | 36 | var percentageData 37 | = ArithmeticAndRelationOperationService.PerformAlgebraOperation( 38 | firstNumericDataMultipliedPerOneHundred, 39 | BinaryOperatorType.Division, 40 | secondNumericData) as INumericData; 41 | 42 | if (percentageData is not null) 43 | { 44 | return Task.FromResult( 45 | (IData?)new PercentageData( 46 | percentageData.LineTextIncludingLineBreak, 47 | percentageData.StartInLine, 48 | percentageData.EndInLine, 49 | percentageData.NumericValueInCurrentUnit / 100)); 50 | } 51 | 52 | return Task.FromResult(null); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.BuiltInPlugins/Functions/Percentage/IsWhatPercentOffInterpreter.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.BuiltInPlugins.Functions.Percentage 2 | { 3 | [Export(typeof(IFunctionInterpreter))] 4 | [Name("percentage.isWhatPercentOff")] 5 | [Culture(SupportedCultures.English)] 6 | internal sealed class IsWhatPercentOffInterpreter : IFunctionInterpreter 7 | { 8 | [Import] 9 | public IArithmeticAndRelationOperationService ArithmeticAndRelationOperationService { get; set; } = null!; 10 | 11 | public Task InterpretFunctionAsync( 12 | string culture, 13 | FunctionDefinition functionDefinition, 14 | IReadOnlyList detectedData, 15 | CancellationToken cancellationToken) 16 | { 17 | Guard.HasSizeEqualTo(detectedData, 2); 18 | detectedData[0].IsOfSubtype("numeric"); 19 | detectedData[1].IsOfType("numeric"); 20 | var firstNumericData = (INumericData)detectedData[0]; 21 | var secondNumericData = (INumericData)detectedData[1]; 22 | 23 | // 1 - (1 / (250 / 62.5)) = 0.75 24 | // so 62.5 represents 250 - 75% (aka. 75% off 25). 25 | 26 | var divisionResult = ArithmeticAndRelationOperationService.PerformAlgebraOperation(secondNumericData, BinaryOperatorType.Division, firstNumericData) as INumericData; 27 | Guard.IsNotNull(divisionResult); 28 | 29 | return Task.FromResult( 30 | (IData?)new PercentageData( 31 | firstNumericData.LineTextIncludingLineBreak, 32 | firstNumericData.StartInLine, 33 | secondNumericData.EndInLine, 34 | 1d - (1d / divisionResult.NumericValueInCurrentUnit))); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.BuiltInPlugins/Functions/Percentage/IsWhatPercentOnInterpreter.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.BuiltInPlugins.Functions.Percentage 2 | { 3 | [Export(typeof(IFunctionInterpreter))] 4 | [Name("percentage.isWhatPercentOn")] 5 | [Culture(SupportedCultures.English)] 6 | internal sealed class IsWhatPercentOnInterpreter : IFunctionInterpreter 7 | { 8 | [Import] 9 | public IArithmeticAndRelationOperationService ArithmeticAndRelationOperationService { get; set; } = null!; 10 | 11 | public Task InterpretFunctionAsync( 12 | string culture, 13 | FunctionDefinition functionDefinition, 14 | IReadOnlyList detectedData, 15 | CancellationToken cancellationToken) 16 | { 17 | Guard.HasSizeEqualTo(detectedData, 2); 18 | detectedData[0].IsOfSubtype("numeric"); 19 | detectedData[1].IsOfType("numeric"); 20 | var firstNumericData = (INumericData)detectedData[0]; 21 | var secondNumericData = (INumericData)detectedData[1]; 22 | 23 | var one 24 | = new DecimalData( 25 | secondNumericData.LineTextIncludingLineBreak, 26 | secondNumericData.StartInLine, 27 | secondNumericData.EndInLine, 28 | 1); 29 | 30 | // ((250 / 62.5) - 1) = 3 31 | // so 250 represents an increase of 300% on 62.5. 32 | 33 | IData? dividedNumbers 34 | = ArithmeticAndRelationOperationService.PerformAlgebraOperation( 35 | firstNumericData, 36 | BinaryOperatorType.Division, 37 | secondNumericData); 38 | 39 | var percentageData 40 | = ArithmeticAndRelationOperationService.PerformAlgebraOperation( 41 | dividedNumbers, 42 | BinaryOperatorType.Subtraction, 43 | one) as INumericData; 44 | 45 | if (percentageData is not null) 46 | { 47 | return Task.FromResult( 48 | (IData?)new PercentageData( 49 | percentageData.LineTextIncludingLineBreak, 50 | percentageData.StartInLine, 51 | percentageData.EndInLine, 52 | percentageData.NumericValueInCurrentUnit)); 53 | } 54 | 55 | return Task.FromResult(null); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.BuiltInPlugins/Functions/Percentage/PercentOfInterpreter.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.BuiltInPlugins.Functions.Percentage 2 | { 3 | [Export(typeof(IFunctionInterpreter))] 4 | [Name("percentage.percentOf")] 5 | [Culture(SupportedCultures.English)] 6 | internal sealed class PercentOfInterpreter : IFunctionInterpreter 7 | { 8 | [Import] 9 | public IArithmeticAndRelationOperationService ArithmeticAndRelationOperationService { get; set; } = null!; 10 | 11 | public Task InterpretFunctionAsync( 12 | string culture, 13 | FunctionDefinition functionDefinition, 14 | IReadOnlyList detectedData, 15 | CancellationToken cancellationToken) 16 | { 17 | Guard.HasSizeEqualTo(detectedData, 2); 18 | detectedData[0].IsOfSubtype("percentage"); 19 | detectedData[1].IsOfType("numeric"); 20 | var percentageData = (INumericData)detectedData[0]; 21 | var numericData = (INumericData)detectedData[1]; 22 | 23 | // x * (% of x) 24 | return Task.FromResult( 25 | ArithmeticAndRelationOperationService.PerformAlgebraOperation( 26 | numericData, BinaryOperatorType.Multiply, percentageData)); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.BuiltInPlugins/Functions/Percentage/PercentOffInterpreter.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.BuiltInPlugins.Functions.Percentage 2 | { 3 | [Export(typeof(IFunctionInterpreter))] 4 | [Name("percentage.percentOff")] 5 | [Culture(SupportedCultures.English)] 6 | internal sealed class PercentOffInterpreter : IFunctionInterpreter 7 | { 8 | [Import] 9 | public IArithmeticAndRelationOperationService ArithmeticAndRelationOperationService { get; set; } = null!; 10 | 11 | public Task InterpretFunctionAsync( 12 | string culture, 13 | FunctionDefinition functionDefinition, 14 | IReadOnlyList detectedData, 15 | CancellationToken cancellationToken) 16 | { 17 | Guard.HasSizeEqualTo(detectedData, 2); 18 | detectedData[0].IsOfSubtype("percentage"); 19 | detectedData[1].IsOfType("numeric"); 20 | var percentageData = (INumericData)detectedData[0]; 21 | var numericData = (INumericData)detectedData[1]; 22 | 23 | // x - (% of x) 24 | return Task.FromResult( 25 | ArithmeticAndRelationOperationService.PerformAlgebraOperation( 26 | numericData, BinaryOperatorType.Subtraction, percentageData)); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.BuiltInPlugins/Functions/Percentage/PercentOnInterpreter.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.BuiltInPlugins.Functions.Percentage 2 | { 3 | [Export(typeof(IFunctionInterpreter))] 4 | [Name("percentage.percentOn")] 5 | [Culture(SupportedCultures.English)] 6 | internal sealed class PercentOnInterpreter : IFunctionInterpreter 7 | { 8 | [Import] 9 | public IArithmeticAndRelationOperationService ArithmeticAndRelationOperationService { get; set; } = null!; 10 | 11 | public Task InterpretFunctionAsync( 12 | string culture, 13 | FunctionDefinition functionDefinition, 14 | IReadOnlyList detectedData, 15 | CancellationToken cancellationToken) 16 | { 17 | Guard.HasSizeEqualTo(detectedData, 2); 18 | detectedData[0].IsOfSubtype("percentage"); 19 | detectedData[1].IsOfType("numeric"); 20 | var percentageData = (INumericData)detectedData[0]; 21 | var numericData = (INumericData)detectedData[1]; 22 | 23 | // x + (% of x) 24 | return Task.FromResult( 25 | ArithmeticAndRelationOperationService.PerformAlgebraOperation( 26 | numericData, BinaryOperatorType.Addition, percentageData)); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.BuiltInPlugins/Grammars/FunctionDefinitionProvider.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using Newtonsoft.Json; 4 | 5 | namespace NotepadBasedCalculator.BuiltInPlugins.Grammars 6 | { 7 | [Export(typeof(IFunctionDefinitionProvider))] 8 | [Culture(SupportedCultures.English)] 9 | public sealed class FunctionDefinitionProvider : IFunctionDefinitionProvider 10 | { 11 | private readonly Dictionary>>> _cultureToFunctionDefinition = new(); 12 | 13 | public IReadOnlyList>> LoadFunctionDefinitions(string culture) 14 | { 15 | culture = culture.Replace("-", "_"); 16 | 17 | lock (_cultureToFunctionDefinition) 18 | { 19 | if (!_cultureToFunctionDefinition.TryGetValue(culture, out List>>? functionDefinitions) || functionDefinitions is null) 20 | { 21 | functionDefinitions = new(); 22 | 23 | Dictionary>? parsedJson 24 | = LoadResource( 25 | $"NotepadBasedCalculator.BuiltInPlugins.Grammars.{culture}.FunctionDefinition.json"); 26 | 27 | if (parsedJson is not null) 28 | { 29 | functionDefinitions.Add(parsedJson); 30 | } 31 | 32 | _cultureToFunctionDefinition[culture] = functionDefinitions; 33 | } 34 | 35 | return functionDefinitions; 36 | } 37 | } 38 | 39 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 40 | private static Dictionary>? LoadResource(string resourceName) 41 | { 42 | var assembly = Assembly.GetExecutingAssembly(); 43 | 44 | using Stream? embeddedResourceStream = assembly.GetManifestResourceStream(resourceName); 45 | if (embeddedResourceStream is null) 46 | { 47 | throw new Exception("Unable to find the UnitNames file."); 48 | } 49 | 50 | using var textStreamReader = new StreamReader(embeddedResourceStream); 51 | 52 | Dictionary>? parsedJson 53 | = JsonConvert.DeserializeObject>>( 54 | textStreamReader.ReadToEnd()); 55 | 56 | return parsedJson; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.BuiltInPlugins/Grammars/GrammarProvider.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using Microsoft.Recognizers.Text; 4 | 5 | namespace NotepadBasedCalculator.BuiltInPlugins 6 | { 7 | [Export(typeof(IGrammarProvider))] 8 | [Culture(SupportedCultures.English)] 9 | internal class GrammarProvider : IGrammarProvider 10 | { 11 | public IReadOnlyList? LoadTokenDefinitionGrammars(string culture) 12 | { 13 | culture = culture.Replace("-", "_"); 14 | 15 | var grammars = new List(); 16 | TokenDefinitionGrammar? grammar = LoadGrammar($"NotepadBasedCalculator.BuiltInPlugins.Grammars.{culture}.TokenDefinition.json"); 17 | if (grammar is not null) 18 | { 19 | grammars.Add(grammar); 20 | } 21 | 22 | grammar = LoadGrammar($"NotepadBasedCalculator.BuiltInPlugins.Grammars.SpecialTokenDefinition.json"); 23 | if (grammar is not null) 24 | { 25 | grammars.Add(grammar); 26 | } 27 | 28 | return grammars; 29 | } 30 | 31 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 32 | private static TokenDefinitionGrammar? LoadGrammar(string resourceName) 33 | { 34 | var assembly = Assembly.GetExecutingAssembly(); 35 | 36 | using Stream? embeddedResourceStream = assembly.GetManifestResourceStream(resourceName); 37 | if (embeddedResourceStream is null) 38 | { 39 | throw new Exception("Unable to find the grammar file."); 40 | } 41 | 42 | using var textStreamReader = new StreamReader(embeddedResourceStream); 43 | 44 | return TokenDefinitionGrammar.Load(textStreamReader.ReadToEnd()); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.BuiltInPlugins/Grammars/SpecialTokenDefinition.json: -------------------------------------------------------------------------------- 1 | { 2 | "common_tokens": { 3 | "comment_operators": [ 4 | "//" 5 | ], 6 | "header_operators": [ 7 | "#", 8 | "##", 9 | "###", 10 | "####", 11 | "#####", 12 | "######" 13 | ] 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.BuiltInPlugins/Grammars/UnitMapProvider.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using NotepadBasedCalculator.BuiltInPlugins.Data; 4 | 5 | namespace NotepadBasedCalculator.BuiltInPlugins.Grammars 6 | { 7 | [Export(typeof(UnitMapProvider))] 8 | [Culture(SupportedCultures.English)] 9 | [Culture(SupportedCultures.French)] 10 | public sealed class UnitMapProvider 11 | { 12 | private readonly Dictionary _cultureToUnitMap = new(); 13 | 14 | internal UnitMap LoadUnitMap(string culture) 15 | { 16 | culture = culture.Replace("-", "_"); 17 | 18 | lock (_cultureToUnitMap) 19 | { 20 | if (!_cultureToUnitMap.TryGetValue(culture, out UnitMap? unitMap) || unitMap is null) 21 | { 22 | unitMap = LoadResource($"NotepadBasedCalculator.BuiltInPlugins.Grammars.{culture}.UnitNames.json"); 23 | _cultureToUnitMap[culture] = unitMap; 24 | } 25 | 26 | return unitMap; 27 | } 28 | } 29 | 30 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 31 | private static UnitMap LoadResource(string resourceName) 32 | { 33 | var assembly = Assembly.GetExecutingAssembly(); 34 | 35 | using Stream? embeddedResourceStream = assembly.GetManifestResourceStream(resourceName); 36 | if (embeddedResourceStream is null) 37 | { 38 | throw new Exception("Unable to find the UnitNames file."); 39 | } 40 | 41 | using var textStreamReader = new StreamReader(embeddedResourceStream); 42 | 43 | return UnitMap.Load(textStreamReader.ReadToEnd()); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.BuiltInPlugins/Grammars/en-us/FunctionDefinition.json: -------------------------------------------------------------------------------- 1 | { 2 | "general": { 3 | "random": [ 4 | "random number between NUMERIC and NUMERIC", 5 | "random between NUMERIC and NUMERIC" 6 | ], 7 | "midpoint": [ 8 | "midpoint between NUMERIC and NUMERIC", 9 | "average between NUMERIC and NUMERIC" 10 | ], 11 | "remainder": [ 12 | "remainder of NUMERIC divided by NUMERIC" 13 | ] 14 | }, 15 | "percentage": { 16 | "percentOf": [ 17 | "PERCENTAGE of NUMERIC" 18 | ], 19 | "percentOff": [ 20 | "PERCENTAGE off NUMERIC" 21 | ], 22 | "percentOn": [ 23 | "PERCENTAGE on NUMERIC" 24 | ], 25 | "isPercentOfWhat": [ 26 | "NUMERIC is PERCENTAGE of what", 27 | "NUMERIC is PERCENTAGE of what number", 28 | "NUMERIC is PERCENTAGE off what", 29 | "NUMERIC is PERCENTAGE off what number", 30 | "PERCENTAGE of what is NUMERIC", 31 | "PERCENTAGE of what number is NUMERIC", 32 | "PERCENTAGE off what is NUMERIC", 33 | "PERCENTAGE off what number is NUMERIC" 34 | ], 35 | "isPercentOnWhat": [ 36 | "NUMERIC is PERCENTAGE on what", 37 | "NUMERIC is PERCENTAGE on what number", 38 | "NUMERIC is what plus PERCENTAGE", 39 | "PERCENTAGE on what is NUMERIC", 40 | "PERCENTAGE on what number is NUMERIC" 41 | ], 42 | "isWhatPercentOn": [ 43 | "NUMERIC is what % on NUMERIC", 44 | "NUMERIC is what percent on NUMERIC", 45 | "NUMERIC is what percentage on NUMERIC", 46 | "NUMERIC as a % on NUMERIC", 47 | "NUMERIC as % on NUMERIC", 48 | "NUMERIC as percent on NUMERIC", 49 | "NUMERIC as a percent on NUMERIC", 50 | "NUMERIC as percentage on NUMERIC", 51 | "NUMERIC as a percentage on NUMERIC" 52 | ], 53 | "isWhatPercentOff": [ 54 | "NUMERIC is what % off NUMERIC", 55 | "NUMERIC is what percent off NUMERIC", 56 | "NUMERIC is what percentage off NUMERIC", 57 | "NUMERIC as a % off NUMERIC", 58 | "NUMERIC as % off NUMERIC", 59 | "NUMERIC as a percent off NUMERIC", 60 | "NUMERIC as percent off NUMERIC", 61 | "NUMERIC as a percentage off NUMERIC", 62 | "NUMERIC as percentage off NUMERIC" 63 | ], 64 | "isWhatPercentOf": [ 65 | "NUMERIC is what % of NUMERIC", 66 | "NUMERIC is what percent of NUMERIC", 67 | "NUMERIC is what percentage of NUMERIC", 68 | "NUMERIC represents what % of NUMERIC", 69 | "NUMERIC represents what percent of NUMERIC", 70 | "NUMERIC represents what percentage of NUMERIC", 71 | "NUMERIC off NUMERIC is what %", 72 | "NUMERIC off NUMERIC is what percent", 73 | "NUMERIC off NUMERIC is what percentage", 74 | "NUMERIC as a % of NUMERIC", 75 | "NUMERIC as % of NUMERIC", 76 | "NUMERIC as a percent of NUMERIC", 77 | "NUMERIC as percent of NUMERIC", 78 | "NUMERIC as a percentage of NUMERIC", 79 | "NUMERIC as percentage of NUMERIC" 80 | ] 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.BuiltInPlugins/Grammars/en-us/TokenDefinition.json: -------------------------------------------------------------------------------- 1 | { 2 | "common_tokens": { 3 | "equal_to_operators": [ 4 | "==", 5 | "equal", 6 | "equals" 7 | ], 8 | "not_equal_to_operators": [ 9 | "!=", 10 | "=!", 11 | "not equal" 12 | ], 13 | "less_than_or_equal_to_operators": [ 14 | "<=", 15 | "=<", 16 | "less than or equal to" 17 | ], 18 | "less_than_operators": [ 19 | "<", 20 | "less than" 21 | ], 22 | "greater_than_or_equal_to_operators": [ 23 | ">=", 24 | "=>", 25 | "greater than or equal to" 26 | ], 27 | "greater_than_operators": [ 28 | ">", 29 | "greater than" 30 | ], 31 | "addition_operators": [ 32 | "+", 33 | "plus", 34 | "and", 35 | "along with" 36 | ], 37 | "substration_operators": [ 38 | "−", // U+2212 39 | "–", // U+2013 40 | "-", // U+002D 41 | "minus" 42 | ], 43 | "multiplication_operators": [ 44 | "*", 45 | "×", 46 | "x", 47 | "⋅", 48 | "·", 49 | "times", 50 | "multiplied by", 51 | "multiply by" 52 | ], 53 | "division_operators": [ 54 | "/", 55 | "÷", 56 | "per", 57 | "divided by", 58 | "divide by" 59 | ], 60 | "if_identifiers": [ 61 | "if" 62 | ], 63 | "then_identifiers": [ 64 | "then" 65 | ], 66 | "else_identifiers": [ 67 | "else" 68 | ], 69 | "boolean_true_identifiers": [ 70 | "true" 71 | ], 72 | "boolean_false_identifiers": [ 73 | "false" 74 | ] 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.BuiltInPlugins/Grammars/fr-fr/UnitNames.json: -------------------------------------------------------------------------------- 1 | { 2 | "Length": { 3 | "Centimètres": "Centimeter", 4 | "Décamètre": "Decameter", 5 | "Décimètres": "Decimeter", 6 | "Pied": "Foot", 7 | "Hectomètre": "Hectometer", 8 | "Pouce": "Inch", 9 | "Kilomètres": "Kilometer", 10 | "Mètres": "Meter", 11 | "Micromètres": "Micrometer", 12 | "Mile": "Mile", 13 | "Millimètres": "Millimeter", 14 | "Nanomètres": "Nanometer", 15 | "Yard": "Yard" 16 | }, 17 | "Information": { 18 | "Bit": "Bit", 19 | "Kilobit": "Kilobit", 20 | "Megabit": "Megabit", 21 | "Mégabit": "Megabit", 22 | "Gigabit": "Gigabit", 23 | "Terabit": "Terabit", 24 | "Petabit": "Petabit", 25 | "octet": "Byte", 26 | "Kilooctet": "Kilobyte", 27 | "Mégaoctet": "Megabyte", 28 | "Gigaoctet": "Gigabyte", 29 | "Téraoctet": "Terabyte", 30 | "Pétaoctet": "Petabyte" 31 | }, 32 | "Area": { 33 | "Acre": "Acre", 34 | "Hectare": "Hectare", 35 | "Hectomètre carré": "Hectare", 36 | "Centimètre carré": "SquareCentimeter", 37 | "Décimètre carré": "SquareDecimeter", 38 | "Pied carré": "SquareFoot", 39 | "Pouce carré": "SquareInch", 40 | "Kilomètre carré": "SquareKilometer", 41 | "Mètre carré": "SquareMeter", 42 | "Mile carré": "SquareMile", 43 | "Millimètre carré": "SquareMillimeter" 44 | }, 45 | "Speed": { 46 | "Mètre par seconde": "MeterPerSecond", 47 | "Kilomètre par heure": "KilometerPerHour", 48 | "Kilomètre par minute": "KilometerPerMinute", 49 | "Kilomètre par seconde": "KilometerPerSecond", 50 | "Miles par heure": "MilePerHour", 51 | "Noeuds": "Knot", 52 | "Pied par seconde": "FootPerSecond", 53 | "Pied par minute": "FootPerMinute" 54 | }, 55 | "Volume": { 56 | "Mètre cube": "CubicMeter", 57 | "Centimètre cube": "CubicCentimeter", 58 | "Millimètre cube": "CubicMillimeter", 59 | "Kilomètre cube": "CubicKilometer", 60 | "Pieds cube": "CubicFoot", 61 | "Litre": "Liter", 62 | "Décilitre": "Deciliter", 63 | "Centilitre": "Centiliter", 64 | "Milliliter": "Milliliter", 65 | "Gallon": "UsGallon", 66 | "Pintes": "UsPint", 67 | "Baril": "OilBarrel", 68 | "Onces": "UsOunce" 69 | }, 70 | "Mass": { 71 | "Kilogramme": "Kilogram", 72 | "Gram": "Gram", 73 | "Milligramme": "Milligram", 74 | "Microgramme": "Microgram", 75 | "Tonne": "Tonne", 76 | "Livre": "Pound" 77 | }, 78 | "Angle": { 79 | "Degree": "Degree", 80 | "Radian": "Radian" 81 | }, 82 | "Temperature": { 83 | "Kelvin": "Kelvin", 84 | "F": "DegreeFahrenheit", 85 | "R": "DegreeRankine", 86 | "C": "DegreeCelsius", 87 | "Degré": "DegreeCelsius" 88 | } 89 | } -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.BuiltInPlugins/NotepadBasedCalculator.BuiltInPlugins.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(NetCoreAndStandard) 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.BuiltInPlugins/StatementParsersAndInterpreters/Comment/CommentStatement.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.BuiltInPlugins.StatementParsersAndInterpreters.Comment 2 | { 3 | internal sealed class CommentStatement : Statement 4 | { 5 | internal CommentStatement(LinkedToken firstToken, LinkedToken lastToken) 6 | : base(firstToken, lastToken) 7 | { 8 | } 9 | 10 | public override string ToString() 11 | { 12 | return "Comment"; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.BuiltInPlugins/StatementParsersAndInterpreters/Comment/CommentStatementParserAndInterpreter.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.BuiltInPlugins.StatementParsersAndInterpreters.Comment 2 | { 3 | [Export(typeof(IStatementParserAndInterpreter))] 4 | [Culture(SupportedCultures.Any)] 5 | [Name(PredefinedStatementParserAndInterpreterNames.CommentStatement)] 6 | internal sealed class CommentStatementParserAndInterpreter : IStatementParserAndInterpreter 7 | { 8 | public IParserAndInterpreterService ParserAndInterpreterService => throw new NotImplementedException(); 9 | 10 | public Task TryParseAndInterpretStatementAsync( 11 | string culture, 12 | LinkedToken currentToken, 13 | IVariableService variableService, 14 | StatementParserAndInterpreterResult result, 15 | CancellationToken cancellationToken) 16 | { 17 | if (currentToken.Token.IsOfType(PredefinedTokenAndDataTypeNames.CommentOperator)) 18 | { 19 | LinkedToken lastTokenInLine = currentToken; 20 | LinkedToken? nextToken = currentToken.Next; 21 | while (nextToken is not null) 22 | { 23 | lastTokenInLine = nextToken; 24 | nextToken = nextToken.Next; 25 | } 26 | 27 | result.ParsedStatement = new CommentStatement(currentToken.SkipNextWordTokens()!, lastTokenInLine); 28 | return Task.FromResult(true); 29 | } 30 | 31 | return Task.FromResult(false); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.BuiltInPlugins/StatementParsersAndInterpreters/Condition/ConditionStatement.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.BuiltInPlugins.StatementParsersAndInterpreters.Condition 2 | { 3 | internal sealed class ConditionStatement : Statement 4 | { 5 | internal Expression Condition { get; } 6 | 7 | internal IData? StatementResult { get; } 8 | 9 | internal ConditionStatement(LinkedToken firstToken, LinkedToken lastToken, Expression condition, IData? statementResult) 10 | : base(firstToken, lastToken) 11 | { 12 | Condition = condition; 13 | StatementResult = statementResult; 14 | } 15 | 16 | public override string ToString() 17 | { 18 | return "Condition"; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.BuiltInPlugins/StatementParsersAndInterpreters/Function/FunctionExpressionStatementParserAndInterpreter.cs: -------------------------------------------------------------------------------- 1 | using NotepadBasedCalculator.BuiltInPlugins.StatementParsersAndInterpreters.NumericalExpression; 2 | 3 | namespace NotepadBasedCalculator.BuiltInPlugins.StatementParsersAndInterpreters.Function 4 | { 5 | [Export(typeof(IStatementParserAndInterpreter))] 6 | [Culture(SupportedCultures.English)] 7 | [Name(PredefinedStatementParserAndInterpreterNames.FunctionExpressionStatement)] 8 | [Order(Before = PredefinedStatementParserAndInterpreterNames.NumericalExpressionStatement)] 9 | internal sealed class FunctionExpressionStatementParserAndInterpreter : IStatementParserAndInterpreter 10 | { 11 | [Import] 12 | public IParserAndInterpreterService ParserAndInterpreterService { get; set; } = null!; 13 | 14 | public async Task TryParseAndInterpretStatementAsync( 15 | string culture, 16 | LinkedToken currentToken, 17 | IVariableService variableService, 18 | StatementParserAndInterpreterResult result, 19 | CancellationToken cancellationToken) 20 | { 21 | var expressionResult = new ExpressionParserAndInterpreterResult(); 22 | 23 | bool functionFound 24 | = await ParserAndInterpreterService.TryParseAndInterpretExpressionAsync( 25 | new[] { PredefinedExpressionParserAndInterpreterNames.FunctionExpression }, 26 | culture, 27 | currentToken, 28 | variableService, 29 | expressionResult, 30 | cancellationToken); 31 | 32 | if (functionFound) 33 | { 34 | result.ParsedStatement 35 | = new NumericalCalculusStatement( 36 | expressionResult.ParsedExpression!.FirstToken, 37 | expressionResult.ParsedExpression.LastToken, 38 | expressionResult.ParsedExpression); 39 | result.ResultedData = expressionResult.ResultedData; 40 | return true; 41 | } 42 | 43 | return false; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.BuiltInPlugins/StatementParsersAndInterpreters/Header/HeaderStatement.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.BuiltInPlugins.StatementParsersAndInterpreters.Header 2 | { 3 | internal sealed class HeaderStatement : Statement 4 | { 5 | internal HeaderStatement(LinkedToken firstToken, LinkedToken lastToken) 6 | : base(firstToken, lastToken) 7 | { 8 | } 9 | 10 | public override string ToString() 11 | { 12 | return "Header"; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.BuiltInPlugins/StatementParsersAndInterpreters/Header/HeaderStatementParserAndInterpreter.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.BuiltInPlugins.StatementParsersAndInterpreters.Header 2 | { 3 | [Export(typeof(IStatementParserAndInterpreter))] 4 | [Culture(SupportedCultures.Any)] 5 | [Name(PredefinedStatementParserAndInterpreterNames.HeaderStatement)] 6 | internal sealed class HeaderStatementParserAndInterpreter : IStatementParserAndInterpreter 7 | { 8 | public IParserAndInterpreterService ParserAndInterpreterService => throw new NotImplementedException(); 9 | 10 | public Task TryParseAndInterpretStatementAsync( 11 | string culture, 12 | LinkedToken currentToken, 13 | IVariableService variableService, 14 | StatementParserAndInterpreterResult result, 15 | CancellationToken cancellationToken) 16 | { 17 | if (currentToken.Token.IsOfType(PredefinedTokenAndDataTypeNames.HeaderOperator)) 18 | { 19 | LinkedToken? previousToken = currentToken.Previous; 20 | LinkedToken firstTokenInLine = currentToken; 21 | while (previousToken is not null) 22 | { 23 | firstTokenInLine = previousToken; 24 | if (previousToken.Token.IsNotOfType(PredefinedTokenAndDataTypeNames.Whitespace)) 25 | { 26 | return Task.FromResult(false); 27 | } 28 | 29 | previousToken = previousToken.Previous; 30 | } 31 | 32 | LinkedToken? nextToken = currentToken.Next; 33 | LinkedToken lastTokenInLine = currentToken; 34 | while (nextToken is not null) 35 | { 36 | lastTokenInLine = nextToken; 37 | nextToken = nextToken.Next; 38 | } 39 | 40 | result.ParsedStatement = new HeaderStatement(firstTokenInLine, lastTokenInLine); 41 | return Task.FromResult(true); 42 | } 43 | 44 | return Task.FromResult(false); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.BuiltInPlugins/StatementParsersAndInterpreters/NumericalExpression/NumericalCalculusStatement.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.BuiltInPlugins.StatementParsersAndInterpreters.NumericalExpression 2 | { 3 | internal sealed class NumericalCalculusStatement : Statement 4 | { 5 | internal Expression NumericalCalculusExpression { get; } 6 | 7 | internal NumericalCalculusStatement(LinkedToken firstToken, LinkedToken lastToken, Expression numericalCalculusExpression) 8 | : base(firstToken, lastToken) 9 | { 10 | NumericalCalculusExpression = numericalCalculusExpression; 11 | } 12 | 13 | public override string ToString() 14 | { 15 | return NumericalCalculusExpression.ToString(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.BuiltInPlugins/StatementParsersAndInterpreters/NumericalExpression/NumericalExpressionStatementParserAndInterpreter.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.BuiltInPlugins.StatementParsersAndInterpreters.NumericalExpression 2 | { 3 | [Export(typeof(IStatementParserAndInterpreter))] 4 | [Culture(SupportedCultures.Any)] 5 | [Name(PredefinedStatementParserAndInterpreterNames.NumericalExpressionStatement)] 6 | [Order(After = PredefinedStatementParserAndInterpreterNames.ConditionExpressionStatement)] 7 | internal sealed class NumericalExpressionStatementParserAndInterpreter : IStatementParserAndInterpreter 8 | { 9 | [Import] 10 | public IParserAndInterpreterService ParserAndInterpreterService { get; set; } = null!; 11 | 12 | public async Task TryParseAndInterpretStatementAsync( 13 | string culture, 14 | LinkedToken currentToken, 15 | IVariableService variableService, 16 | StatementParserAndInterpreterResult result, 17 | CancellationToken cancellationToken) 18 | { 19 | var expressionResult = new ExpressionParserAndInterpreterResult(); 20 | 21 | bool expressionFound 22 | = await ParserAndInterpreterService.TryParseAndInterpretExpressionAsync( 23 | new[] { PredefinedExpressionParserAndInterpreterNames.NumericalExpression }, 24 | culture, 25 | currentToken, 26 | variableService, 27 | expressionResult, 28 | cancellationToken); 29 | 30 | if (expressionFound 31 | && expressionResult.ParsedExpression 32 | is DataExpression 33 | or VariableReferenceExpression 34 | or GroupExpression 35 | or BinaryOperatorExpression) 36 | { 37 | if (expressionResult.ParsedExpression is BinaryOperatorExpression binaryOperatorExpression 38 | && !(binaryOperatorExpression.Operator is BinaryOperatorType.Addition or BinaryOperatorType.Division or BinaryOperatorType.Multiply or BinaryOperatorType.Subtraction)) 39 | { 40 | return false; 41 | } 42 | 43 | result.ParsedStatement 44 | = new NumericalCalculusStatement( 45 | expressionResult.ParsedExpression!.FirstToken, 46 | expressionResult.ParsedExpression.LastToken, 47 | expressionResult.ParsedExpression); 48 | result.ResultedData = expressionResult.ResultedData; 49 | return true; 50 | } 51 | 52 | return false; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Core/Core/DescendingComparer.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Core 2 | { 3 | internal sealed class DescendingComparer : IComparer where T : IComparable 4 | { 5 | public int Compare(T? x, T? y) 6 | { 7 | return y!.CompareTo(x!); 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Core/Logger.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Core 2 | { 3 | [Export(typeof(ILogger))] 4 | internal sealed class Logger : ILogger 5 | { 6 | public void Log(string logName, params (string, string?)[]? properties) 7 | { 8 | // TODO 9 | } 10 | 11 | public void Log(string logName, string? description, params (string, string?)[]? properties) 12 | { 13 | // TODO 14 | } 15 | 16 | public void LogFault(string logName, Exception ex, params (string, object?)[]? properties) 17 | { 18 | // TODO 19 | } 20 | 21 | public void LogFault(string logName, Exception ex, string description, params (string, object?)[]? properties) 22 | { 23 | // TODO 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Core/Mef/MefComposer.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.Composition.Hosting; 2 | using System.Reflection; 3 | using NotepadBasedCalculator.BuiltInPlugins.Data; 4 | 5 | namespace NotepadBasedCalculator.Core.Mef 6 | { 7 | /// 8 | /// Provides a set of methods to initialize and manage MEF. 9 | /// 10 | internal sealed class MefComposer : IDisposable 11 | { 12 | private readonly Assembly[] _assemblies; 13 | private readonly object[] _customExports; 14 | private bool _isExportProviderDisposed = true; 15 | 16 | public IMefProvider Provider { get; } 17 | 18 | public ExportProvider ExportProvider { get; private set; } 19 | 20 | public MefComposer(Assembly[]? assemblies = null, params object[] customExports) 21 | { 22 | if (Provider is not null) 23 | { 24 | throw new InvalidOperationException("Mef composer already initialized."); 25 | } 26 | 27 | _assemblies = assemblies ?? Array.Empty(); 28 | _customExports = customExports ?? Array.Empty(); 29 | ExportProvider = InitializeMef(); 30 | 31 | Provider = ExportProvider.GetExport()!.Value; 32 | ((MefProvider)Provider).ExportProvider = ExportProvider; 33 | } 34 | 35 | public void Dispose() 36 | { 37 | if (ExportProvider is not null) 38 | { 39 | ((CompositionContainer)ExportProvider).Dispose(); 40 | } 41 | 42 | _isExportProviderDisposed = true; 43 | } 44 | 45 | internal void Reset() 46 | { 47 | // For unit tests. 48 | Dispose(); 49 | InitializeMef(); 50 | } 51 | 52 | private ExportProvider InitializeMef() 53 | { 54 | if (!_isExportProviderDisposed) 55 | { 56 | return ExportProvider; 57 | } 58 | 59 | var assemblies = new HashSet(_assemblies) 60 | { 61 | Assembly.GetExecutingAssembly(), 62 | typeof(NumberDataParser).Assembly 63 | }; 64 | 65 | var catalog = new AggregateCatalog(); 66 | foreach (Assembly assembly in assemblies) 67 | { 68 | catalog.Catalogs.Add(new AssemblyCatalog(assembly)); 69 | } 70 | 71 | var container = new CompositionContainer(catalog); 72 | var batch = new CompositionBatch(); 73 | batch.AddPart(this); 74 | 75 | for (int i = 0; i < _customExports.Length; i++) 76 | { 77 | batch.AddPart(_customExports[i]); 78 | } 79 | 80 | container.Compose(batch); 81 | 82 | ExportProvider = container; 83 | 84 | _isExportProviderDisposed = false; 85 | 86 | return ExportProvider; 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Core/Mef/MefProvider.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.Composition.Hosting; 2 | 3 | namespace NotepadBasedCalculator.Core.Mef 4 | { 5 | [Export(typeof(IMefProvider))] 6 | internal sealed class MefProvider : IMefProvider 7 | { 8 | internal ExportProvider? ExportProvider { get; set; } 9 | 10 | public TExport Import() 11 | { 12 | return ExportProvider!.GetExport()!.Value; 13 | } 14 | 15 | public IEnumerable ImportMany() 16 | { 17 | return ExportProvider!.GetExports().Select(e => e.Value); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Core/NotepadBasedCalculator.Core.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(NetCoreAndStandard) 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Core/ParserAndInterpreterFactory.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Core 2 | { 3 | [Export] 4 | internal sealed class ParserAndInterpreterFactory 5 | { 6 | private readonly ILogger _logger; 7 | private readonly ILexer _lexer; 8 | private readonly IParserAndInterpretersRepository _parserRepository; 9 | private readonly IArithmeticAndRelationOperationService _arithmeticAndRelationOperationService; 10 | 11 | [ImportingConstructor] 12 | public ParserAndInterpreterFactory( 13 | ILogger logger, 14 | ILexer lexer, 15 | IParserAndInterpretersRepository parserRepository, 16 | IArithmeticAndRelationOperationService arithmeticAndRelationOperationService) 17 | { 18 | _logger = logger; 19 | _lexer = lexer; 20 | _parserRepository = parserRepository; 21 | _arithmeticAndRelationOperationService = arithmeticAndRelationOperationService; 22 | } 23 | 24 | internal ParserAndInterpreter CreateInstance(string culture, TextDocument textDocument) 25 | { 26 | return new ParserAndInterpreter( 27 | culture, 28 | _logger, 29 | _lexer, 30 | _parserRepository, 31 | _arithmeticAndRelationOperationService, 32 | textDocument); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Core/ParserAndInterpreterResultUpdatedEventArgs.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Core 2 | { 3 | internal sealed class ParserAndInterpreterResultUpdatedEventArgs : DeferredCancelEventArgs 4 | { 5 | internal IReadOnlyList? ResultPerLines { get; } 6 | 7 | public ParserAndInterpreterResultUpdatedEventArgs(IReadOnlyList? resultPerLines) 8 | { 9 | ResultPerLines = resultPerLines; 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Core/TextDocument.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Core 2 | { 3 | /// 4 | /// Represents a text document. 5 | /// 6 | internal sealed class TextDocument 7 | { 8 | private string _text = string.Empty; 9 | 10 | internal string Text 11 | { 12 | get => _text; 13 | set 14 | { 15 | _text = value ?? string.Empty; 16 | TextChanged?.Invoke(this, EventArgs.Empty); 17 | } 18 | } 19 | 20 | internal event EventHandler? TextChanged; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Desktop.Mac/AppDelegate.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Desktop.Mac 2 | { 3 | [Register("AppDelegate")] 4 | public class AppDelegate : NSApplicationDelegate 5 | { 6 | private static bool isInitialized; 7 | 8 | internal static AppDelegate? Instance { get; private set; } 9 | 10 | internal static AppDelegate Init() 11 | { 12 | if (!isInitialized) 13 | { 14 | isInitialized = true; 15 | NSApplication.Init(); 16 | Instance = new(); 17 | NSApplication.SharedApplication.Delegate = Instance; 18 | } 19 | 20 | Guard.IsNotNull(Instance); 21 | return Instance!; 22 | } 23 | 24 | public override void DidFinishLaunching(NSNotification notification) 25 | { 26 | // Insert code here to initialize your application 27 | } 28 | 29 | public override void WillTerminate(NSNotification notification) 30 | { 31 | // Insert code here to tear down your application 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Desktop.Mac/Assets.xcassets/AppIcon.appiconset/AppIcon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veler/notepad-based-calculator/0748213d0dae153dc8d736a90a0db74f5c4766bd/src/app/dev/NotepadBasedCalculator.Desktop.Mac/Assets.xcassets/AppIcon.appiconset/AppIcon-128.png -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Desktop.Mac/Assets.xcassets/AppIcon.appiconset/AppIcon-128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veler/notepad-based-calculator/0748213d0dae153dc8d736a90a0db74f5c4766bd/src/app/dev/NotepadBasedCalculator.Desktop.Mac/Assets.xcassets/AppIcon.appiconset/AppIcon-128@2x.png -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Desktop.Mac/Assets.xcassets/AppIcon.appiconset/AppIcon-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veler/notepad-based-calculator/0748213d0dae153dc8d736a90a0db74f5c4766bd/src/app/dev/NotepadBasedCalculator.Desktop.Mac/Assets.xcassets/AppIcon.appiconset/AppIcon-16.png -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Desktop.Mac/Assets.xcassets/AppIcon.appiconset/AppIcon-16@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veler/notepad-based-calculator/0748213d0dae153dc8d736a90a0db74f5c4766bd/src/app/dev/NotepadBasedCalculator.Desktop.Mac/Assets.xcassets/AppIcon.appiconset/AppIcon-16@2x.png -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Desktop.Mac/Assets.xcassets/AppIcon.appiconset/AppIcon-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veler/notepad-based-calculator/0748213d0dae153dc8d736a90a0db74f5c4766bd/src/app/dev/NotepadBasedCalculator.Desktop.Mac/Assets.xcassets/AppIcon.appiconset/AppIcon-256.png -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Desktop.Mac/Assets.xcassets/AppIcon.appiconset/AppIcon-256@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veler/notepad-based-calculator/0748213d0dae153dc8d736a90a0db74f5c4766bd/src/app/dev/NotepadBasedCalculator.Desktop.Mac/Assets.xcassets/AppIcon.appiconset/AppIcon-256@2x.png -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Desktop.Mac/Assets.xcassets/AppIcon.appiconset/AppIcon-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veler/notepad-based-calculator/0748213d0dae153dc8d736a90a0db74f5c4766bd/src/app/dev/NotepadBasedCalculator.Desktop.Mac/Assets.xcassets/AppIcon.appiconset/AppIcon-32.png -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Desktop.Mac/Assets.xcassets/AppIcon.appiconset/AppIcon-32@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veler/notepad-based-calculator/0748213d0dae153dc8d736a90a0db74f5c4766bd/src/app/dev/NotepadBasedCalculator.Desktop.Mac/Assets.xcassets/AppIcon.appiconset/AppIcon-32@2x.png -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Desktop.Mac/Assets.xcassets/AppIcon.appiconset/AppIcon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veler/notepad-based-calculator/0748213d0dae153dc8d736a90a0db74f5c4766bd/src/app/dev/NotepadBasedCalculator.Desktop.Mac/Assets.xcassets/AppIcon.appiconset/AppIcon-512.png -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Desktop.Mac/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veler/notepad-based-calculator/0748213d0dae153dc8d736a90a0db74f5c4766bd/src/app/dev/NotepadBasedCalculator.Desktop.Mac/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Desktop.Mac/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "filename": "AppIcon-16.png", 5 | "size": "16x16", 6 | "scale": "1x", 7 | "idiom": "mac" 8 | }, 9 | { 10 | "filename": "AppIcon-16@2x.png", 11 | "size": "16x16", 12 | "scale": "2x", 13 | "idiom": "mac" 14 | }, 15 | { 16 | "filename": "AppIcon-32.png", 17 | "size": "32x32", 18 | "scale": "1x", 19 | "idiom": "mac" 20 | }, 21 | { 22 | "filename": "AppIcon-32@2x.png", 23 | "size": "32x32", 24 | "scale": "2x", 25 | "idiom": "mac" 26 | }, 27 | { 28 | "filename": "AppIcon-128.png", 29 | "size": "128x128", 30 | "scale": "1x", 31 | "idiom": "mac" 32 | }, 33 | { 34 | "filename": "AppIcon-128@2x.png", 35 | "size": "128x128", 36 | "scale": "2x", 37 | "idiom": "mac" 38 | }, 39 | { 40 | "filename": "AppIcon-256.png", 41 | "size": "256x256", 42 | "scale": "1x", 43 | "idiom": "mac" 44 | }, 45 | { 46 | "filename": "AppIcon-256@2x.png", 47 | "size": "256x256", 48 | "scale": "2x", 49 | "idiom": "mac" 50 | }, 51 | { 52 | "filename": "AppIcon-512.png", 53 | "size": "512x512", 54 | "scale": "1x", 55 | "idiom": "mac" 56 | }, 57 | { 58 | "filename": "AppIcon-512@2x.png", 59 | "size": "512x512", 60 | "scale": "2x", 61 | "idiom": "mac" 62 | } 63 | ], 64 | "info": { 65 | "version": 1, 66 | "author": "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Desktop.Mac/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Desktop.Mac/Entitlements.plist: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Desktop.Mac/Info.plist: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | CFBundleName 6 | NotepadBasedCalculator 7 | CFBundleIdentifier 8 | com.etiennebaudoux.NotepadBasedCalculator 9 | CFBundleShortVersionString 10 | 1.0 11 | CFBundleVersion 12 | 1 13 | CFBundleDevelopmentRegion 14 | en 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundlePackageType 18 | APPL 19 | CFBundleSignature 20 | ???? 21 | NSHumanReadableCopyright 22 | ${AuthorCopyright:HtmlEncode} 23 | NSPrincipalClass 24 | NSApplication 25 | XSAppIconAssets 26 | Assets.xcassets/AppIcon.appiconset 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Desktop.Mac/NotepadBasedCalculator.Desktop.Mac.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | $(NetCoreMac) 6 | true 7 | 8 | copyused 9 | true 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Desktop.Mac/PlatformInitializer.cs: -------------------------------------------------------------------------------- 1 | using NotepadBasedCalculator.Desktop.Platform; 2 | 3 | namespace NotepadBasedCalculator.Desktop.Mac 4 | { 5 | [Export(typeof(IPlatformInitializer))] 6 | internal sealed class PlatformInitializer : IPlatformInitializer 7 | { 8 | private bool _isInitialized; 9 | 10 | public void Initialize() 11 | { 12 | if (!_isInitialized) 13 | { 14 | _isInitialized = true; 15 | AppDelegate.Init(); 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Desktop.Mac/Program.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using NotepadBasedCalculator.Core.Mef; 4 | 5 | namespace NotepadBasedCalculator.Desktop.Mac 6 | { 7 | internal class Program 8 | { 9 | // Initialization code. Don't use any Avalonia, third-party APIs or any 10 | // SynchronizationContext-reliant code before AppMain is called: things aren't initialized 11 | // yet and stuff might break. 12 | [STAThread] 13 | public static void Main(string[] args) 14 | { 15 | BuildAvaloniaApp() 16 | .StartWithClassicDesktopLifetime( 17 | args, 18 | shutdownMode: ShutdownMode.OnMainWindowClose); 19 | } 20 | 21 | // Avalonia configuration, don't remove; also used by visual designer. 22 | public static AppBuilder BuildAvaloniaApp() 23 | { 24 | return 25 | AppBuilder.Configure() 26 | .UsePlatformDetect() 27 | .LogToTrace() 28 | .With(new X11PlatformOptions 29 | { 30 | EnableMultiTouch = true 31 | }) 32 | .With(new MefComposer(new[] 33 | { 34 | typeof(Program).Assembly, 35 | typeof(App).Assembly 36 | }).Provider); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Desktop.Mac/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Versioning; 2 | 3 | [assembly: SupportedOSPlatform("macos10.14")] 4 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Desktop.Windows/NotepadBasedCalculator.Desktop.Windows.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | $(NetCoreWindows) 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Desktop.Windows/PlatformInitializer.cs: -------------------------------------------------------------------------------- 1 | using NotepadBasedCalculator.Desktop.Platform; 2 | 3 | namespace NotepadBasedCalculator.Desktop.Windows 4 | { 5 | [Export(typeof(IPlatformInitializer))] 6 | internal sealed class PlatformInitializer : IPlatformInitializer 7 | { 8 | private bool _isInitialized; 9 | 10 | public void Initialize() 11 | { 12 | if (!_isInitialized) 13 | { 14 | _isInitialized = true; 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Desktop.Windows/Program.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using NotepadBasedCalculator.Core.Mef; 3 | 4 | namespace NotepadBasedCalculator.Desktop.Windows 5 | { 6 | internal class Program 7 | { 8 | // Initialization code. Don't use any Avalonia, third-party APIs or any 9 | // SynchronizationContext-reliant code before AppMain is called: things aren't initialized 10 | // yet and stuff might break. 11 | [STAThread] 12 | public static void Main(string[] args) 13 | { 14 | BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); 15 | } 16 | 17 | // Avalonia configuration, don't remove; also used by visual designer. 18 | public static AppBuilder BuildAvaloniaApp() 19 | { 20 | return 21 | AppBuilder.Configure() 22 | .UsePlatformDetect() 23 | .LogToTrace() 24 | .With(new Win32PlatformOptions 25 | { 26 | UseWindowsUIComposition = true, 27 | CompositionBackdropCornerRadius = 8f, 28 | }) 29 | .With(new MefComposer(new[] 30 | { 31 | typeof(Program).Assembly, 32 | typeof(App).Assembly 33 | }).Provider); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Desktop.Windows/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Versioning; 2 | 3 | [assembly: SupportedOSPlatform("windows10.0.17763.0")] 4 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Desktop/App.axaml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Desktop/App.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls.ApplicationLifetimes; 3 | using Avalonia.Markup.Xaml; 4 | using NotepadBasedCalculator.Desktop.Platform; 5 | 6 | namespace NotepadBasedCalculator.Desktop 7 | { 8 | public partial class App : Application 9 | { 10 | public override void Initialize() 11 | { 12 | AvaloniaXamlLoader.Load(this); 13 | } 14 | 15 | public override void OnFrameworkInitializationCompleted() 16 | { 17 | if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) 18 | { 19 | IMefProvider mefProvider = AvaloniaLocator.Current.GetService()!; 20 | Guard.IsNotNull(mefProvider); 21 | mefProvider.Import().Initialize(); 22 | 23 | desktop.MainWindow = new MainWindow(); 24 | } 25 | 26 | base.OnFrameworkInitializationCompleted(); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Desktop/MainWindow.axaml: -------------------------------------------------------------------------------- 1 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 23 | 24 | 29 | 30 | 31 | 32 | 62 | 63 | 64 |