Skip to content

Commit

Permalink
Add sample for extending calculator
Browse files Browse the repository at this point in the history
  • Loading branch information
Dmytro Barbul committed Jun 11, 2022
1 parent 8dfa080 commit 31b537f
Show file tree
Hide file tree
Showing 9 changed files with 212 additions and 3 deletions.
16 changes: 15 additions & 1 deletion Calculator.sln
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,16 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Calculator.Collections", "l
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Calculator.Collections.Tests", "lib\Calculator.Collections.Tests\Calculator.Collections.Tests.csproj", "{DE089AD5-8385-439E-9D15-2EC4762336EA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Calculator.Samples.SimlpeUsage", "samples\Calculator.Samples.SimlpeUsage\Calculator.Samples.SimlpeUsage.csproj", "{09A64D23-BCCD-481F-B53D-25A30469C14D}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Calculator.Samples.SimpleUsage", "samples\Calculator.Samples.SimpleUsage\Calculator.Samples.SimpleUsage.csproj", "{09A64D23-BCCD-481F-B53D-25A30469C14D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Calculator.Core.Tests", "tests\Calculator.Core.Tests\Calculator.Core.Tests.csproj", "{8E9D8A84-A02D-495A-8616-3529D74FBC55}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Calculator.Extra.Tests", "tests\Calculator.Extra.Tests\Calculator.Extra.Tests.csproj", "{C6FB1D32-30CF-43CF-9BD7-8B72366EEC76}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Calculator.Samples.ExtendingCalculator.Tests", "samples\Calculator.Samples.ExtendingCalculator.Tests\Calculator.Samples.ExtendingCalculator.Tests.csproj", "{07DB7E64-2D97-498C-9DF4-6D2B197A30EE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Calculator.Samples.ExtendingCalculator", "samples\Calculator.Samples.ExtendingCalculator\Calculator.Samples.ExtendingCalculator.csproj", "{75B3675F-A7F5-4B28-A725-BD2A47918874}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -63,6 +67,14 @@ Global
{C6FB1D32-30CF-43CF-9BD7-8B72366EEC76}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C6FB1D32-30CF-43CF-9BD7-8B72366EEC76}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C6FB1D32-30CF-43CF-9BD7-8B72366EEC76}.Release|Any CPU.Build.0 = Release|Any CPU
{07DB7E64-2D97-498C-9DF4-6D2B197A30EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{07DB7E64-2D97-498C-9DF4-6D2B197A30EE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{07DB7E64-2D97-498C-9DF4-6D2B197A30EE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{07DB7E64-2D97-498C-9DF4-6D2B197A30EE}.Release|Any CPU.Build.0 = Release|Any CPU
{75B3675F-A7F5-4B28-A725-BD2A47918874}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{75B3675F-A7F5-4B28-A725-BD2A47918874}.Debug|Any CPU.Build.0 = Debug|Any CPU
{75B3675F-A7F5-4B28-A725-BD2A47918874}.Release|Any CPU.ActiveCfg = Release|Any CPU
{75B3675F-A7F5-4B28-A725-BD2A47918874}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -73,5 +85,7 @@ Global
{09A64D23-BCCD-481F-B53D-25A30469C14D} = {4EBD3981-43B0-49B0-B22A-232B83C375AE}
{8E9D8A84-A02D-495A-8616-3529D74FBC55} = {A197C227-A78B-4920-9397-1D5AEF40865D}
{C6FB1D32-30CF-43CF-9BD7-8B72366EEC76} = {A197C227-A78B-4920-9397-1D5AEF40865D}
{07DB7E64-2D97-498C-9DF4-6D2B197A30EE} = {4EBD3981-43B0-49B0-B22A-232B83C375AE}
{75B3675F-A7F5-4B28-A725-BD2A47918874} = {4EBD3981-43B0-49B0-B22A-232B83C375AE}
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.1.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Calculator.Samples.ExtendingCalculator\Calculator.Samples.ExtendingCalculator.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using Calculator.Core.Operands;
using Calculator.Core.ParsingContexts;
using Calculator.Core.Tokens;
using Calculator.Samples.ExtendingCalculator.Parsers;

namespace Calculator.Samples.ExtendingCalculator.Tests.Parsers;

public class StringOperandParserTest
{
private readonly StringOperandParser parser = new();

[Fact]
public void TryParse_SimpleString_Parsed()
{
bool isParsed = this.parser.TryParse("\"test\"", ParsingContext.Initial, out Token? token, out int parsedLength);

Assert.True(isParsed);
Operand<string> stringOperand = Assert.IsType<Operand<string>>(token);
Assert.Equal(6, parsedLength);
Assert.Equal("test", stringOperand.Value);
}

[Fact]
public void TryParse_EscapedQuotes_Parsed()
{
bool isParsed = this.parser.TryParse("\"test \\\" test\" something else", ParsingContext.Initial, out Token? token, out int parsedLength);

Assert.True(isParsed);
Operand<string> stringOperand = Assert.IsType<Operand<string>>(token);
Assert.Equal(14, parsedLength);
Assert.Equal("test \" test", stringOperand.Value);
}

[Fact]
public void TryParse_EscapedBackslash_Parsed()
{
bool isParsed = this.parser.TryParse("\"test\\\\\" test\" something else", ParsingContext.Initial, out Token? token, out int parsedLength);

Assert.True(isParsed);
Operand<string> stringOperand = Assert.IsType<Operand<string>>(token);
Assert.Equal(8, parsedLength);
Assert.Equal("test\\", stringOperand.Value);
}

[Fact]
public void TryParse_NoClosingQuote_NotParsed()
{
bool isParsed = this.parser.TryParse("\"test", ParsingContext.Initial, out Token? token, out _);

Assert.False(isParsed);
Assert.Null(token);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
global using Xunit;
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Calculator.Core\Calculator.Core.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using Calculator.Core.Operands;
using Calculator.Core.Tokens;

namespace Calculator.Samples.ExtendingCalculator.Operators;

public class LengthOperator : UnaryOperator
{
public override string Text => "||";

protected override Operand ExecuteUnaryOperator(Operand operand)
{
return operand switch
{
ListOperand listOperand => this.GetLengthOfList(listOperand),
Operand<string> stringOperand => this.GetLengthOfString(stringOperand),
_ => throw new ArgumentException("Length operator can be performed only on list or string"),
};
}

private Operand GetLengthOfList(ListOperand listOperand)
{
decimal sqrSum = 0;
foreach (Operand listOperandOperand in listOperand.Operands)
{
if (listOperandOperand is not Operand<decimal> decimalOperand)
{
throw new ArgumentException($"Operator {this.Text} can only be performed on list of decimals");
}

sqrSum += decimalOperand.Value * decimalOperand.Value;
}

return new Operand<decimal>((decimal)Math.Sqrt((double)sqrSum));
}

private Operand GetLengthOfString(Operand<string> stringOperand)
{
return new Operand<decimal>(stringOperand.Value.Length);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using System.Diagnostics.CodeAnalysis;
using Calculator.Core.Operands;
using Calculator.Core.Parsers;
using Calculator.Core.ParsingContexts;
using Calculator.Core.Tokens;

namespace Calculator.Samples.ExtendingCalculator.Parsers;

public class StringOperandParser : IParser
{
public bool TryParse(
ReadOnlySpan<char> formula,
ParsingContext context,
[NotNullWhen(true)] out Token? token,
out int parsedLength)
{
token = null;
parsedLength = default;

if (formula[0] != '"')
{
return false;
}

bool isEscaped = false;
int i;
for (i = 1; i < formula.Length; i++)
{
if (formula[i] == '\\' && !isEscaped)
{
isEscaped = true;
}
else if (formula[i] == '"' && !isEscaped)
{
break;
}
else
{
isEscaped = false;
}
}

if (i == formula.Length)
{
return false;
}

token = new Operand<string>(
formula[1..i]
.ToString()
.Replace("\\\"", "\"")
.Replace("\\\\", "\\"));
parsedLength = i + 1;

return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<ItemGroup>
<ProjectReference Include="..\..\src\Calculator.Core.DependencyInjection\Calculator.Core.DependencyInjection.csproj" />
<ProjectReference Include="..\..\src\Calculator.Extra\Calculator.Extra.csproj" />
<ProjectReference Include="..\Calculator.Samples.ExtendingCalculator\Calculator.Samples.ExtendingCalculator.csproj" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
using Calculator.Core.DependencyInjection;
using Calculator.Extra.Operators;
using Calculator.Samples.ExtendingCalculator.Operators;
using Microsoft.Extensions.DependencyInjection;

namespace Calculator.Samples.SimlpeUsage;
namespace Calculator.Samples.SimpleUsage;

public static class Program
{
Expand Down Expand Up @@ -42,7 +43,9 @@ public static void Main(string[] args)
private static IServiceProvider SetupDI()
{
return new ServiceCollection()
.AddCalculator(typeof(AddOperator).Assembly)
.AddCalculator(
typeof(AddOperator).Assembly,
typeof(LengthOperator).Assembly)
.BuildServiceProvider(validateScopes: true);
}
}

0 comments on commit 31b537f

Please sign in to comment.