├── tools ├── app-version-number.txt ├── Install-DotNet.sh └── Install-DotNet.ps1 ├── screenshot.png ├── .nuke └── parameters.json ├── src ├── build │ ├── PlatformTarget.cs │ ├── Directory.Build.targets │ ├── AppVersion │ │ ├── CSharpVersionUpdateRule.cs │ │ ├── AppVersion.cs │ │ ├── VersionString.cs │ │ ├── VersionUpdateRule.cs │ │ ├── CSharpUpdater.cs │ │ └── AppxManifestUpdater.cs │ ├── Configuration.cs │ ├── .editorconfig │ ├── _build.csproj │ ├── DotnetParameters.cs │ └── Directory.Build.props ├── app │ ├── dev │ │ ├── NotepadBasedCalculator.Desktop.Mac │ │ │ ├── Assets.xcassets │ │ │ │ ├── Contents.json │ │ │ │ └── AppIcon.appiconset │ │ │ │ │ ├── AppIcon-16.png │ │ │ │ │ ├── AppIcon-32.png │ │ │ │ │ ├── AppIcon-128.png │ │ │ │ │ ├── AppIcon-256.png │ │ │ │ │ ├── AppIcon-512.png │ │ │ │ │ ├── AppIcon-128@2x.png │ │ │ │ │ ├── AppIcon-16@2x.png │ │ │ │ │ ├── AppIcon-256@2x.png │ │ │ │ │ ├── AppIcon-32@2x.png │ │ │ │ │ ├── AppIcon-512@2x.png │ │ │ │ │ └── Contents.json │ │ │ ├── Properties │ │ │ │ └── AssemblyInfo.cs │ │ │ ├── Entitlements.plist │ │ │ ├── PlatformInitializer.cs │ │ │ ├── Info.plist │ │ │ ├── AppDelegate.cs │ │ │ ├── NotepadBasedCalculator.Desktop.Mac.csproj │ │ │ └── Program.cs │ │ ├── NotepadBasedCalculator.Desktop.Windows │ │ │ ├── Properties │ │ │ │ └── AssemblyInfo.cs │ │ │ ├── PlatformInitializer.cs │ │ │ ├── NotepadBasedCalculator.Desktop.Windows.csproj │ │ │ └── Program.cs │ │ ├── Directory.Build.targets │ │ ├── NotepadBasedCalculator.Api │ │ │ ├── Lexer │ │ │ │ ├── ITokenEnumerator.cs │ │ │ │ ├── ILexer.cs │ │ │ │ ├── LinkedToken.cs │ │ │ │ └── TokenizedTextLine.cs │ │ │ ├── Data │ │ │ │ ├── IDecimal.cs │ │ │ │ ├── ISupportMultipleDataTypeForArithmeticOperation.cs │ │ │ │ ├── IValueRelativeToOtherData.cs │ │ │ │ ├── Definition │ │ │ │ │ └── CurrencyValue.cs │ │ │ │ ├── IDataParser.cs │ │ │ │ ├── IData.cs │ │ │ │ ├── INumericData.cs │ │ │ │ └── Data.cs │ │ │ ├── Metadata │ │ │ │ ├── IOrderableMetadata.cs │ │ │ │ ├── NameAttribute.cs │ │ │ │ ├── FunctionInterpreterMetadata.cs │ │ │ │ ├── CultureAttribute.cs │ │ │ │ ├── CultureCodeMetadata.cs │ │ │ │ ├── SupportedCultures.cs │ │ │ │ ├── OrderAttribute.cs │ │ │ │ └── ParserAndInterpreterMetadata.cs │ │ │ ├── DataOperationException.cs │ │ │ ├── AbstractSyntaxTree │ │ │ │ ├── Statement.cs │ │ │ │ ├── Expression.cs │ │ │ │ ├── ReferenceExpression.cs │ │ │ │ ├── DataExpression.cs │ │ │ │ ├── FunctionExpression.cs │ │ │ │ ├── VariableReferenceExpression.cs │ │ │ │ ├── AbstractSyntaxTreeBase.cs │ │ │ │ ├── GroupExpression.cs │ │ │ │ ├── VariableDeclarationStatement.cs │ │ │ │ ├── BinaryOperatorExpression.cs │ │ │ │ └── BinaryOperatorType.cs │ │ │ ├── Grammar │ │ │ │ ├── IGrammarProvider.cs │ │ │ │ ├── IFunctionDefinitionProvider.cs │ │ │ │ ├── TokenDefinitionGrammar.cs │ │ │ │ └── FunctionDefinition.cs │ │ │ ├── IncompatibleUnitsException.cs │ │ │ ├── Core │ │ │ │ ├── DictionaryExtensions.cs │ │ │ │ ├── CultureHelper.cs │ │ │ │ ├── Threading │ │ │ │ │ ├── CancellationTokenExtension.cs │ │ │ │ │ ├── AsyncLazy.cs │ │ │ │ │ ├── DisposableSempahore.cs │ │ │ │ │ └── TaskExtension.cs │ │ │ │ ├── DictionaryWithSpecialEnumValueConverter.cs │ │ │ │ └── EnumExtension.cs │ │ │ ├── UnsupportedArithmeticOperationException.cs │ │ │ ├── IMefProvider.cs │ │ │ ├── NotepadBasedCalculator.Api.csproj │ │ │ ├── ILogger.cs │ │ │ ├── ParserAndInterpreter │ │ │ │ ├── PredefinedExpressionParserAndInterpreterNames.cs │ │ │ │ ├── PredefinedStatementParserAndInterpreterNames.cs │ │ │ │ ├── IVariableService.cs │ │ │ │ ├── StatementParserAndInterpreterResult.cs │ │ │ │ ├── ExpressionParserAndInterpreterResult.cs │ │ │ │ ├── IFunctionInterpreter.cs │ │ │ │ ├── IParserAndInterpretersRepository.cs │ │ │ │ ├── IStatementParserAndInterpreter.cs │ │ │ │ └── IExpressionParserAndInterpreter.cs │ │ │ ├── ICurrencyService.cs │ │ │ └── IArithmeticAndRelationOperationService.cs │ │ ├── NotepadBasedCalculator.Desktop │ │ │ ├── Platform │ │ │ │ ├── Services │ │ │ │ │ ├── Theme │ │ │ │ │ │ ├── UserPreferredTheme.cs │ │ │ │ │ │ ├── AppTheme.cs │ │ │ │ │ │ └── IThemeService.cs │ │ │ │ │ └── IUiService.cs │ │ │ │ └── IPlatformInitializer.cs │ │ │ ├── App.axaml │ │ │ ├── App.axaml.cs │ │ │ ├── NotepadBasedCalculator.Desktop.csproj │ │ │ ├── MainWindow.axaml.cs │ │ │ └── MainWindow.axaml │ │ ├── NotepadBasedCalculator.BuiltInPlugins │ │ │ ├── Grammars │ │ │ │ ├── SpecialTokenDefinition.json │ │ │ │ ├── en-us │ │ │ │ │ ├── TokenDefinition.json │ │ │ │ │ └── FunctionDefinition.json │ │ │ │ ├── UnitMapProvider.cs │ │ │ │ ├── GrammarProvider.cs │ │ │ │ ├── FunctionDefinitionProvider.cs │ │ │ │ └── fr-fr │ │ │ │ │ └── UnitNames.json │ │ │ ├── StatementParsersAndInterpreters │ │ │ │ ├── Header │ │ │ │ │ ├── HeaderStatement.cs │ │ │ │ │ └── HeaderStatementParserAndInterpreter.cs │ │ │ │ ├── Comment │ │ │ │ │ ├── CommentStatement.cs │ │ │ │ │ └── CommentStatementParserAndInterpreter.cs │ │ │ │ ├── Condition │ │ │ │ │ └── ConditionStatement.cs │ │ │ │ ├── NumericalExpression │ │ │ │ │ ├── NumericalCalculusStatement.cs │ │ │ │ │ └── NumericalExpressionStatementParserAndInterpreter.cs │ │ │ │ └── Function │ │ │ │ │ └── FunctionExpressionStatementParserAndInterpreter.cs │ │ │ ├── Functions │ │ │ │ ├── General │ │ │ │ │ ├── RemainderInterpreter.cs │ │ │ │ │ ├── MidpointInterpreter.cs │ │ │ │ │ └── RandomNumberInterpreter.cs │ │ │ │ └── Percentage │ │ │ │ │ ├── PercentOfInterpreter.cs │ │ │ │ │ ├── PercentOnInterpreter.cs │ │ │ │ │ ├── PercentOffInterpreter.cs │ │ │ │ │ ├── IsWhatPercentOffInterpreter.cs │ │ │ │ │ ├── IsPercentOnWhatInterpreter.cs │ │ │ │ │ ├── IsPercentOfWhatInterpreter.cs │ │ │ │ │ ├── IsWhatPercentOfInterpreter.cs │ │ │ │ │ └── IsWhatPercentOnInterpreter.cs │ │ │ ├── Data │ │ │ │ ├── BooleanDataParser.cs │ │ │ │ ├── UnitMap.cs │ │ │ │ ├── OrdinalDataParser.cs │ │ │ │ ├── TemperatureDataParser.cs │ │ │ │ ├── NumberDataParser.cs │ │ │ │ └── CurrencyDataParser.cs │ │ │ └── NotepadBasedCalculator.BuiltInPlugins.csproj │ │ ├── NotepadBasedCalculator.Core │ │ │ ├── Core │ │ │ │ └── DescendingComparer.cs │ │ │ ├── ParserAndInterpreterResultUpdatedEventArgs.cs │ │ │ ├── TextDocument.cs │ │ │ ├── Mef │ │ │ │ ├── MefProvider.cs │ │ │ │ └── MefComposer.cs │ │ │ ├── Logger.cs │ │ │ ├── ParserAndInterpreterFactory.cs │ │ │ └── NotepadBasedCalculator.Core.csproj │ │ ├── shared │ │ │ ├── GlobalUsings.cs │ │ │ ├── SharedAssemblyVersion.cs │ │ │ └── SharedAssemblyInfo.cs │ │ └── Directory.Build.props │ ├── tools │ │ ├── NotepadBasedCalculator.Benchmark │ │ │ ├── Program.cs │ │ │ ├── NotepadBasedCalculator.Benchmark.csproj │ │ │ └── CalculatorBenchmarks.cs │ │ └── Directory.Build.props │ └── tests │ │ ├── NotepadBasedCalculator.Core.Tests │ │ ├── TestHelper.cs │ │ ├── NotepadBasedCalculator.Core.Tests.csproj │ │ └── MefBaseTest.cs │ │ └── NotepadBasedCalculator.StandaloneConsoleTestApp │ │ └── NotepadBasedCalculator.StandaloneConsoleTestApp.csproj ├── Directory.Build.targets ├── Environment.props └── Directory.Build.props ├── init.cmd ├── .vscode ├── tasks.json └── launch.json ├── nuget.config ├── LICENSE.md ├── init.sh ├── init.ps1 └── .gitattributes /tools/app-version-number.txt: -------------------------------------------------------------------------------- 1 | 0.0.0.0 -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veler/notepad-based-calculator/HEAD/screenshot.png -------------------------------------------------------------------------------- /.nuke/parameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./build.schema.json", 3 | "Solution": "src/NotepadBasedCalculator.App.sln" 4 | } -------------------------------------------------------------------------------- /src/build/PlatformTarget.cs: -------------------------------------------------------------------------------- 1 | enum PlatformTarget 2 | { 3 | Windows, 4 | MacOS, 5 | Linux, 6 | iOS, 7 | Android 8 | } 9 | -------------------------------------------------------------------------------- /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/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Versioning; 2 | 3 | [assembly: SupportedOSPlatform("macos10.14")] 4 | -------------------------------------------------------------------------------- /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/Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Api/Lexer/ITokenEnumerator.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Api 2 | { 3 | internal interface ITokenEnumerator : IEnumerator 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Desktop.Mac/Assets.xcassets/AppIcon.appiconset/AppIcon-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veler/notepad-based-calculator/HEAD/src/app/dev/NotepadBasedCalculator.Desktop.Mac/Assets.xcassets/AppIcon.appiconset/AppIcon-16.png -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Desktop.Mac/Assets.xcassets/AppIcon.appiconset/AppIcon-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veler/notepad-based-calculator/HEAD/src/app/dev/NotepadBasedCalculator.Desktop.Mac/Assets.xcassets/AppIcon.appiconset/AppIcon-32.png -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Desktop.Mac/Assets.xcassets/AppIcon.appiconset/AppIcon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veler/notepad-based-calculator/HEAD/src/app/dev/NotepadBasedCalculator.Desktop.Mac/Assets.xcassets/AppIcon.appiconset/AppIcon-128.png -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Desktop.Mac/Assets.xcassets/AppIcon.appiconset/AppIcon-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veler/notepad-based-calculator/HEAD/src/app/dev/NotepadBasedCalculator.Desktop.Mac/Assets.xcassets/AppIcon.appiconset/AppIcon-256.png -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Desktop.Mac/Assets.xcassets/AppIcon.appiconset/AppIcon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veler/notepad-based-calculator/HEAD/src/app/dev/NotepadBasedCalculator.Desktop.Mac/Assets.xcassets/AppIcon.appiconset/AppIcon-512.png -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Desktop.Mac/Assets.xcassets/AppIcon.appiconset/AppIcon-128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veler/notepad-based-calculator/HEAD/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@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veler/notepad-based-calculator/HEAD/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@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veler/notepad-based-calculator/HEAD/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@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veler/notepad-based-calculator/HEAD/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@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veler/notepad-based-calculator/HEAD/src/app/dev/NotepadBasedCalculator.Desktop.Mac/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Desktop.Mac/Entitlements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Desktop/Platform/Services/Theme/UserPreferredTheme.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Desktop.Platform.Services.Theme 2 | { 3 | internal enum UserPreferredTheme 4 | { 5 | Light, 6 | Dark, 7 | Auto 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Desktop/Platform/Services/Theme/AppTheme.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Desktop.Platform.Services.Theme 2 | { 3 | internal enum AppTheme 4 | { 5 | Light, 6 | Dark, 7 | LightHighContrast, 8 | DarkHighContrast 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /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/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.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.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/tools/NotepadBasedCalculator.Benchmark/Program.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Running; 2 | 3 | namespace NotepadBasedCalculator.Benchmark 4 | { 5 | internal class Program 6 | { 7 | public static void Main(string[] args) 8 | { 9 | new BenchmarkSwitcher(typeof(Program).Assembly).Run(args); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /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/tests/NotepadBasedCalculator.Core.Tests/TestHelper.cs: -------------------------------------------------------------------------------- 1 | using NotepadBasedCalculator.Api; 2 | 3 | namespace NotepadBasedCalculator.Core.Tests 4 | { 5 | public static class TestHelper 6 | { 7 | public static string GetDataDisplayText(this IData data) 8 | { 9 | return data.GetDisplayText(SupportedCultures.English); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/app/tools/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | enable 7 | disable 8 | false 9 | 10 | -------------------------------------------------------------------------------- /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/NotepadBasedCalculator.Desktop/App.axaml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.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 | } -------------------------------------------------------------------------------- /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/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/shared/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using global::CommunityToolkit.Common.Deferred; 2 | global using global::CommunityToolkit.Diagnostics; 3 | global using global::NotepadBasedCalculator.Api; 4 | global using global::System; 5 | global using global::System.ComponentModel; 6 | global using global::System.ComponentModel.Composition; 7 | global using global::System.Diagnostics; 8 | global using ExportAttribute = global::System.ComponentModel.Composition.ExportAttribute; 9 | -------------------------------------------------------------------------------- /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/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/build/Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Desktop/Platform/IPlatformInitializer.cs: -------------------------------------------------------------------------------- 1 | namespace NotepadBasedCalculator.Desktop.Platform 2 | { 3 | /// 4 | /// Provides an entry point for the app to start-up anything platform-specific. 5 | /// 6 | internal interface IPlatformInitializer 7 | { 8 | /// 9 | /// Initializes some platform-specific work. 10 | /// 11 | void Initialize(); 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/build/AppVersion/CSharpVersionUpdateRule.cs: -------------------------------------------------------------------------------- 1 | internal class CSharpVersionUpdateRule 2 | { 3 | private readonly VersionUpdateRule _updateRule; 4 | public CSharpVersionUpdateRule(string attributeName, string updateRule) 5 | { 6 | AttributeName = attributeName; 7 | _updateRule = new VersionUpdateRule(updateRule); 8 | } 9 | public string AttributeName { get; private set; } 10 | public string Update(VersionString v) { return _updateRule.Update(v); } 11 | } 12 | -------------------------------------------------------------------------------- /nuget.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /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/build/Configuration.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using Nuke.Common.Tooling; 3 | 4 | [TypeConverter(typeof(TypeConverter))] 5 | internal sealed class Configuration : Enumeration 6 | { 7 | public static readonly Configuration Debug = new() { Value = nameof(Debug) }; 8 | public static readonly Configuration Release = new() { Value = nameof(Release) }; 9 | 10 | public static implicit operator string(Configuration configuration) 11 | { 12 | return configuration.Value; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /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/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.Desktop/Platform/Services/IUiService.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Styling; 2 | using NotepadBasedCalculator.Desktop.Platform.Services.Theme; 3 | 4 | namespace NotepadBasedCalculator.Desktop.Platform.Services 5 | { 6 | internal interface IUiService 7 | { 8 | /// 9 | /// Gets the list of UI styles to use on the platform, applying to the given . 10 | /// 11 | IEnumerable GetPlatformSpecificStyles(AppTheme theme); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /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/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.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.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.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.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.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.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/build/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.cs] 2 | dotnet_style_qualification_for_field = false:warning 3 | dotnet_style_qualification_for_property = false:warning 4 | dotnet_style_qualification_for_method = false:warning 5 | dotnet_style_qualification_for_event = false:warning 6 | dotnet_style_require_accessibility_modifiers = never:warning 7 | 8 | csharp_style_expression_bodied_methods = true:silent 9 | csharp_style_expression_bodied_properties = true:warning 10 | csharp_style_expression_bodied_indexers = true:warning 11 | csharp_style_expression_bodied_accessors = true:warning 12 | -------------------------------------------------------------------------------- /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/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.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.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/build/_build.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | $(NetCore) 6 | 7 | CS0649;CS0169 8 | ..\.. 9 | ..\.. 10 | 1 11 | false 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /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.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.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.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.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/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/NotepadBasedCalculator.Api.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(NetCoreAndStandard) 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /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/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/build/DotnetParameters.cs: -------------------------------------------------------------------------------- 1 | using Nuke.Common.IO; 2 | 3 | internal sealed record DotnetParameters 4 | { 5 | internal readonly AbsolutePath ProjectOrSolutionPath; 6 | 7 | internal readonly string RuntimeIdentifier; 8 | 9 | internal readonly string TargetFramework; 10 | 11 | internal readonly bool PublishTrimmed; 12 | 13 | public DotnetParameters(AbsolutePath projectOrSolutionPath, string runtimeIdentifier, string targetFramework, bool publishTrimmed) 14 | { 15 | ProjectOrSolutionPath = projectOrSolutionPath; 16 | RuntimeIdentifier = runtimeIdentifier; 17 | TargetFramework = targetFramework; 18 | PublishTrimmed = publishTrimmed; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /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/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/tools/NotepadBasedCalculator.Benchmark/NotepadBasedCalculator.Benchmark.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | 6 | 7 | $(MSBuildThisFileDirectory)\bin\ 8 | Exe 9 | enable 10 | disable 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /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/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/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/tests/NotepadBasedCalculator.StandaloneConsoleTestApp/NotepadBasedCalculator.StandaloneConsoleTestApp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | $(NetCore) 6 | 7 | Exe 8 | enable 9 | enable 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /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.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.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.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.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/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/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/build/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | bin\$(Configuration)\$(Platform)\ 10 | $(BaseOutputPath)$(MSBuildProjectName)\ 11 | obj\$(Platform)\$(MSBuildProjectName)\ 12 | $(BaseIntermediateOutputPath) 13 | $(BaseIntermediateOutputPath)Generated Files\ 14 | 15 | 16 | -------------------------------------------------------------------------------- /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.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/shared/SharedAssemblyVersion.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // 5 | // Changes to this file may cause incorrect behavior and will be lost if 6 | // the code is regenerated. 7 | // 8 | //------------------------------------------------------------------------------ 9 | 10 | using System.Reflection; 11 | 12 | // Version information for an assembly consists of the following four values: 13 | // 14 | // Major Version 15 | // Minor Version 16 | // Build Number 17 | // Revision 18 | // 19 | // You can specify all the values or you can default the Build and Revision Numbers 20 | // by using the '*' as shown below: 21 | // [assembly: AssemblyVersion("1.0.*")] 22 | 23 | // Please DO NOT commit changes on the 2 lines below unless 24 | // you're about to release a new version of the app on the Store. 25 | [assembly: AssemblyVersion("0.0.0.0")] 26 | [assembly: AssemblyFileVersion("0.0.0.0")] 27 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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. -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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.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.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/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.Desktop/Platform/Services/Theme/IThemeService.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Media; 2 | 3 | namespace NotepadBasedCalculator.Desktop.Platform.Services.Theme 4 | { 5 | /// 6 | /// Represents a service for managing the theme of the application. 7 | /// 8 | internal interface IThemeService 9 | { 10 | /// 11 | /// Gets or sets the theme defined by the user. 12 | /// 13 | UserPreferredTheme UserPreferredTheme { get; set; } 14 | 15 | /// 16 | /// Gets the theme to use in the app. 17 | /// 18 | AppTheme AppliedAppTheme { get; } 19 | 20 | /// 21 | /// Gets the system's accent color. 22 | /// 23 | Color AccentColor { get; } 24 | 25 | /// 26 | /// Raised when has changed. 27 | /// 28 | event EventHandler AppliedAppThemeChanged; 29 | 30 | /// 31 | /// Raised when has changed. 32 | /// 33 | event EventHandler AccentColorChanged; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/app/tests/NotepadBasedCalculator.Core.Tests/NotepadBasedCalculator.Core.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | $(NetCore) 6 | 7 | false 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | runtime; build; native; contentfiles; analyzers; buildtransitive 16 | all 17 | 18 | 19 | runtime; build; native; contentfiles; analyzers; buildtransitive 20 | all 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /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.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/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.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.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.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/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.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.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.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.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.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.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 | -------------------------------------------------------------------------------- /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 "---------------------------------------" -------------------------------------------------------------------------------- /src/app/dev/shared/SharedAssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // 5 | // Changes to this file may cause incorrect behavior and will be lost if 6 | // the code is regenerated. 7 | // 8 | //------------------------------------------------------------------------------ 9 | 10 | using System.Reflection; 11 | using System.Runtime.CompilerServices; 12 | 13 | // General Information about an assembly is controlled through the following 14 | // set of attributes. Change these attribute values to modify the information 15 | // associated with an assembly. 16 | [assembly: AssemblyDescription("")] 17 | [assembly: AssemblyConfiguration("")] 18 | [assembly: AssemblyCompany("Etienne BAUDOUX")] 19 | [assembly: AssemblyProduct("Notepad-Based-Calculator")] 20 | [assembly: AssemblyCopyright("© Etienne BAUDOUX. All rights reserved.")] 21 | [assembly: AssemblyTrademark("")] 22 | [assembly: AssemblyCulture("")] 23 | 24 | [assembly: InternalsVisibleTo("NotepadBasedCalculator.Core")] 25 | [assembly: InternalsVisibleTo("NotepadBasedCalculator.Core.Tests")] 26 | [assembly: InternalsVisibleTo("NotepadBasedCalculator.Desktop")] 27 | [assembly: InternalsVisibleTo("NotepadBasedCalculator.Desktop.Mac")] 28 | [assembly: InternalsVisibleTo("NotepadBasedCalculator.Desktop.Windows")] 29 | [assembly: InternalsVisibleTo("NotepadBasedCalculator.StandaloneConsoleTestApp")] 30 | [assembly: InternalsVisibleTo("NotepadBasedCalculator.Benchmark")] 31 | -------------------------------------------------------------------------------- /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.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.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/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.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.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.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/build/AppVersion/AppVersion.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using Nuke.Common.IO; 4 | using Serilog; 5 | 6 | internal static class AppVersion 7 | { 8 | internal static void SetAppVersion(AbsolutePath rootDirectory) 9 | { 10 | string appVersion = GetAppVersion(rootDirectory); 11 | 12 | var csharpUpdater = new CSharpUpdater(appVersion); 13 | IReadOnlyCollection assemblyVersionFiles 14 | = rootDirectory.GlobFiles("**/*AssemblyVersion.cs"); 15 | foreach (AbsolutePath file in assemblyVersionFiles) 16 | { 17 | Log.Information("Updating app version in {File}...", file); 18 | csharpUpdater.UpdateFile(file); 19 | } 20 | 21 | var appxManifestUpdater = new AppxManifestUpdater(appVersion); 22 | IReadOnlyCollection appxmanifestFiles 23 | = rootDirectory.GlobFiles("**/*.appxmanifest"); 24 | foreach (AbsolutePath file in appxmanifestFiles) 25 | { 26 | Log.Information("Updating app version in {File}...", file); 27 | appxManifestUpdater.UpdateFile(file); 28 | } 29 | } 30 | 31 | private static string GetAppVersion(AbsolutePath rootDirectory) 32 | { 33 | AbsolutePath appVersionNumberFile = rootDirectory / "tools" / "app-version-number.txt"; 34 | if (!appVersionNumberFile.FileExists()) 35 | { 36 | Log.Error("Unable to find the app version number in {AppVersionNumberFile}...", appVersionNumberFile); 37 | throw new FileNotFoundException("Unable to find the app version number file.", appVersionNumberFile.ToString()); 38 | } 39 | 40 | return File.ReadAllText(appVersionNumberFile); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /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.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.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.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.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.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/build/AppVersion/VersionString.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.RegularExpressions; 3 | 4 | internal sealed class VersionString 5 | { 6 | internal VersionString() 7 | { 8 | Major = "0"; 9 | Minor = "0"; 10 | Build = "0"; 11 | Revision = "0"; 12 | } 13 | 14 | public VersionString(string version) 15 | { 16 | if (!Parse(version)) 17 | { 18 | throw new ArgumentException("Invalid version string"); 19 | } 20 | } 21 | 22 | private bool Parse(string input) 23 | { 24 | string pattern = @"^(?\d+)\.(?\d+)\.(?:(?:(?\d+)\.(?\*|\d+))|(?\*|\d+))$"; 25 | var regex = new Regex(pattern); 26 | Match match = regex.Match(input); 27 | if (match.Success) 28 | { 29 | Major = match.Groups["Major"].Value; 30 | Minor = match.Groups["Minor"].Value; 31 | Build = match.Groups["Build"].Value; 32 | Revision = match.Groups["Revision"].Value; 33 | } 34 | return match.Success; 35 | } 36 | 37 | public static bool TryParse(string input, out VersionString version) 38 | { 39 | var temp = new VersionString(); 40 | version = null; 41 | if (temp.Parse(input)) 42 | { 43 | version = temp; 44 | } 45 | return version != null; 46 | } 47 | 48 | public string Major { get; set; } 49 | public string Minor { get; set; } 50 | public string Build { get; set; } 51 | public string Revision { get; set; } 52 | 53 | public override string ToString() 54 | { 55 | return string.Format("{0}.{1}{2}{3}", 56 | Major, Minor, 57 | string.IsNullOrEmpty(Build) ? "" : "." + Build, 58 | string.IsNullOrEmpty(Revision) ? "" : "." + Revision); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /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/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.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/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/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/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/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/tests/NotepadBasedCalculator.Core.Tests/MefBaseTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using Moq; 5 | using NotepadBasedCalculator.Api; 6 | using NotepadBasedCalculator.Core.Mef; 7 | 8 | namespace NotepadBasedCalculator.Core.Tests 9 | { 10 | public abstract class MefBaseTest : IDisposable 11 | { 12 | private readonly MefComposer _mefComposer; 13 | 14 | private bool _isDisposed; 15 | 16 | protected IMefProvider ExportProvider { get; } 17 | 18 | protected MefBaseTest() 19 | { 20 | // Do all the tests in English. 21 | // LanguageManager.Instance.SetCurrentCulture(new LanguageDefinition("en-US")); 22 | 23 | var mockCurrencyService = new Mock 24 | { 25 | CallBase = true 26 | }; 27 | mockCurrencyService.Setup(s => s.LoadLatestRatesAsync()) 28 | .Returns( 29 | Task.FromResult( 30 | new Dictionary 31 | { 32 | { "USD", 1 }, 33 | { "CAD", 1.31 } 34 | })); 35 | 36 | _mefComposer 37 | = new MefComposer(null, mockCurrencyService.As().Object); 38 | 39 | ExportProvider = _mefComposer.ExportProvider.GetExport()!.Value; 40 | } 41 | 42 | ~MefBaseTest() 43 | { 44 | Dispose(false); 45 | } 46 | 47 | public void Dispose() 48 | { 49 | Dispose(true); 50 | GC.SuppressFinalize(this); 51 | } 52 | 53 | protected virtual void Dispose(bool disposing) 54 | { 55 | if (_isDisposed) 56 | { 57 | return; 58 | } 59 | 60 | if (disposing) 61 | { 62 | _mefComposer.Dispose(); 63 | } 64 | 65 | _isDisposed = true; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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 | } -------------------------------------------------------------------------------- /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.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.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.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/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.Desktop/NotepadBasedCalculator.Desktop.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(NetCore) 5 | 6 | copyused 7 | true 8 | true 9 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /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/build/AppVersion/VersionUpdateRule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | internal sealed class VersionUpdateRule 5 | { 6 | private readonly string[] _partRules; 7 | public VersionUpdateRule(string rule) 8 | { 9 | _partRules = rule.Split('.'); 10 | if (_partRules.Length < 2 || _partRules.Length > 4) 11 | { 12 | throw new ArgumentException("Expecting 2-4 version parts"); 13 | } 14 | foreach (string partRule in _partRules) 15 | { 16 | if (partRule == "+" || partRule == "=") 17 | { 18 | // OK, valid rule 19 | } 20 | else 21 | { 22 | // will throw an exception if not an int 23 | int.Parse(partRule); 24 | } 25 | } 26 | } 27 | 28 | public string Update(string version) 29 | { 30 | return Update(new VersionString(version)); 31 | } 32 | 33 | public string Update(VersionString version) 34 | { 35 | var inParts = new List() { version.Major, version.Minor, version.Build, version.Revision }; 36 | var outParts = new List(); 37 | for (int index = 0; index < _partRules.Length; index++) 38 | { 39 | string rule = _partRules[index]; 40 | string inPart = inParts[index]; 41 | if (rule == "=") 42 | { 43 | if (inPart.Length > 0) 44 | { 45 | outParts.Add(inParts[index]); 46 | } 47 | } 48 | else if (rule == "+") 49 | { 50 | if (inPart.Length == 0) 51 | { 52 | throw new ArgumentException("Can't increment missing value"); 53 | } 54 | _ = int.TryParse(inPart, out int inNumber); // * gets turned into a zero 55 | inNumber++; 56 | outParts.Add(inNumber.ToString()); 57 | } 58 | else 59 | { 60 | // must be a numeric literal 61 | outParts.Add(_partRules[index]); 62 | } 63 | } 64 | return string.Join(".", outParts.ToArray()); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /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.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/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 | -------------------------------------------------------------------------------- /tools/Install-DotNet.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | bash --version 2>&1 | head -n 1 4 | 5 | set -eo pipefail 6 | SCRIPT_DIR=$1 7 | 8 | if ! [[ -f "$SCRIPT_DIR//.gitignore" ]]; then 9 | echo "Please run this script from the repository's root folder" 10 | exit -1 11 | fi 12 | 13 | ########################################################################### 14 | # CONFIGURATION 15 | ########################################################################### 16 | 17 | TEMP_DIRECTORY="$SCRIPT_DIR//.nuke/temp" 18 | 19 | DOTNET_GLOBAL_FILE="$SCRIPT_DIR//global.json" 20 | DOTNET_INSTALL_URL="https://dot.net/v1/dotnet-install.sh" 21 | DOTNET_CHANNEL="Current" 22 | 23 | export DOTNET_CLI_TELEMETRY_OPTOUT=1 24 | export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 25 | export DOTNET_MULTILEVEL_LOOKUP=0 26 | 27 | ########################################################################### 28 | # EXECUTION 29 | ########################################################################### 30 | 31 | function FirstJsonValue { 32 | perl -nle 'print $1 if m{"'"$1"'": "([^"]+)",?}' <<< "${@:2}" 33 | } 34 | 35 | # If dotnet CLI is installed globally and it matches requested version, use for execution 36 | if [ -x "$(command -v dotnet)" ] && dotnet --version &>/dev/null; then 37 | export DOTNET_EXE="$(command -v dotnet)" 38 | else 39 | # Download install script 40 | DOTNET_INSTALL_FILE="$TEMP_DIRECTORY/dotnet-install.sh" 41 | mkdir -p "$TEMP_DIRECTORY" 42 | curl -Lsfo "$DOTNET_INSTALL_FILE" "$DOTNET_INSTALL_URL" 43 | chmod +x "$DOTNET_INSTALL_FILE" 44 | 45 | # If global.json exists, load expected version 46 | if [[ -f "$DOTNET_GLOBAL_FILE" ]]; then 47 | DOTNET_VERSION=$(FirstJsonValue "version" "$(cat "$DOTNET_GLOBAL_FILE")") 48 | if [[ "$DOTNET_VERSION" == "" ]]; then 49 | unset DOTNET_VERSION 50 | fi 51 | fi 52 | 53 | # Install by channel or version 54 | DOTNET_DIRECTORY="$TEMP_DIRECTORY/dotnet-unix" 55 | if [[ -z ${DOTNET_VERSION+x} ]]; then 56 | "$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --channel "$DOTNET_CHANNEL" --no-path 57 | else 58 | "$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --version "$DOTNET_VERSION" --no-path 59 | fi 60 | export DOTNET_EXE="$DOTNET_DIRECTORY/dotnet" 61 | fi 62 | 63 | echo "Microsoft (R) .NET SDK version $("$DOTNET_EXE" --version)" 64 | echo "--------------------------------------" 65 | -------------------------------------------------------------------------------- /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/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.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/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/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.Desktop/MainWindow.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Threading; 4 | using NotepadBasedCalculator.Core; 5 | using NotepadBasedCalculator.Desktop.Platform.Services.Theme; 6 | 7 | namespace NotepadBasedCalculator.Desktop 8 | { 9 | public partial class MainWindow : Window 10 | { 11 | //private readonly TextDocument _textDocument = new(); 12 | //private readonly ParserAndInterpreter _parserAndInterpreter; 13 | 14 | //public ObservableCollection ResultList { get; } = new(); 15 | 16 | public MainWindow() 17 | { 18 | InitializeComponent(); 19 | 20 | IMefProvider mefProvider = AvaloniaLocator.Current.GetService()!; 21 | Guard.IsNotNull(mefProvider); 22 | mefProvider.Import(); 23 | 24 | //ParserAndInterpreterFactory parserAndInterpreterFactory = mefProvider.GetExport()!.Value; 25 | //_parserAndInterpreter = parserAndInterpreterFactory.CreateInstance(SupportedCultures.English, _textDocument); 26 | //Editor.Document.TextChanged += Document_TextChanged; 27 | //_parserAndInterpreter.ParserAndInterpreterResultUpdated += ParserAndInterpreter_ParserAndInterpreterResultUpdated; 28 | } 29 | 30 | private void Document_TextChanged(object? sender, EventArgs e) 31 | { 32 | //_textDocument.Text = Editor.Document.Text; 33 | } 34 | 35 | private void ParserAndInterpreter_ParserAndInterpreterResultUpdated(object? sender, ParserAndInterpreterResultUpdatedEventArgs e) 36 | { 37 | Dispatcher.UIThread.Post(() => 38 | { 39 | //if (!e.Cancel) 40 | //{ 41 | // ResultList.Clear(); 42 | // for (int i = 0; i < e.ResultPerLines?.Count; i++) 43 | // { 44 | // ParserAndInterpreterResultLine item = e.ResultPerLines[i]; 45 | // if (item.SummarizedResultData is not null) 46 | // { 47 | // ResultList.Add(item.SummarizedResultData.GetDisplayText(SupportedCultures.English)); 48 | // } 49 | // else 50 | // { 51 | // ResultList.Add(" "); 52 | // } 53 | // } 54 | //} 55 | }); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /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/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/build/AppVersion/CSharpUpdater.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Text.RegularExpressions; 3 | using System.IO; 4 | using System; 5 | 6 | internal sealed class CSharpUpdater 7 | { 8 | private readonly List _updateRules; 9 | 10 | internal CSharpUpdater(string version) 11 | { 12 | _updateRules = new List(); 13 | if (!string.IsNullOrEmpty(version)) 14 | { 15 | _updateRules.Add(new CSharpVersionUpdateRule("AssemblyVersion", version)); 16 | _updateRules.Add(new CSharpVersionUpdateRule("AssemblyFileVersion", version)); 17 | } 18 | } 19 | 20 | public void UpdateFile(string fileName) 21 | { 22 | string[] lines = File.ReadAllLines(fileName); 23 | 24 | var outlines = new List(); 25 | foreach (string line in lines) 26 | { 27 | outlines.Add(UpdateLine(line)); 28 | } 29 | 30 | File.WriteAllLines(fileName, outlines.ToArray()); 31 | } 32 | 33 | private string UpdateLine(string line) 34 | { 35 | foreach (CSharpVersionUpdateRule rule in _updateRules) 36 | { 37 | if (UpdateLineWithRule(ref line, rule)) 38 | { 39 | break; 40 | } 41 | } 42 | return line; 43 | } 44 | 45 | private static bool UpdateLineWithRule(ref string line, CSharpVersionUpdateRule rule) 46 | { 47 | bool updated = false; 48 | Group g = GetVersionString(line, rule.AttributeName); 49 | if (g != null) 50 | { 51 | if (VersionString.TryParse(g.Value, out VersionString v) && v is not null) 52 | { 53 | string newVersion = rule.Update(v); 54 | line = string.Concat(line.AsSpan(0, g.Index), newVersion, line.AsSpan(g.Index + g.Length)); 55 | updated = true; 56 | } 57 | } 58 | 59 | return updated; 60 | } 61 | 62 | private static Group GetVersionString(string input, string attributeName) 63 | { 64 | int commentIndex = input.IndexOf("//"); 65 | if (commentIndex != -1) 66 | { 67 | input = input.Substring(0, commentIndex); 68 | } 69 | string attributeMatch = string.Format("(?:(?:{0})|(?:{0}Attribute))", attributeName); 70 | 71 | string pattern = @"^\s*\[assembly: " + attributeMatch + @"\(""(?[0-9\.\*]+)""\)\]"; 72 | var regex = new Regex(pattern); 73 | Match m = regex.Match(input); 74 | if (m.Success) 75 | { 76 | return m.Groups["Version"]; 77 | } 78 | return null; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /src/app/dev/NotepadBasedCalculator.Desktop/MainWindow.axaml: -------------------------------------------------------------------------------- 1 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 23 | 24 | 29 | 30 | 31 | 32 | 62 | 63 | 64 |