Skip to content

[WIP] Maths Tests & Final API #242

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 24 commits into from
Jul 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
coverage:
range: "85...100"
status:
project:
Maths:
paths:
- src/Maths
4 changes: 4 additions & 0 deletions azure-pipelines-cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ jobs:
inputs:
version: $(dotnetCoreVersion)
includePreviewVersions: true
- script: dotnet restore
displayName: 'Restore'
- script: dotnet test --configuration $(buildConfiguration)
displayName: 'Build/Test'
- script: dotnet pack --configuration $(buildConfiguration)
displayName: 'Build/Pack'
- script: dotnet nuget push **/Silk.NET.*.nupkg -s https://api.nuget.org/v3/index.json -k $(NUGET_API_KEY) -n true --skip-duplicate true
Expand Down
22 changes: 22 additions & 0 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,28 @@ jobs:
inputs:
includePreviewVersions: true
version: $(dotnetCoreVersion)

# Silk.NET.Maths Tests
- script: dotnet run --project ./src/Maths/Silk.NET.Maths.Tests/Silk.NET.Maths.Tests.fsproj --configuration Release -- --nunit-summary TestResults.xml
displayName: 'Run Tests and Export NUnit'
- task: PublishTestResults@2
condition: succeededOrFailed()
displayName: 'Publish Maths Tests'
inputs:
testRunner: NUnit
testResultsFiles: 'TestResults.xml'
- script: dotnet test ./src/Maths/Silk.NET.Maths.Tests/Silk.NET.Maths.Tests.fsproj --configuration Release /p:AltCover=true /p:AltCoverCobertura="cobertura.xml" -- --nunit-summary TestResults.xml
displayName: 'Collect Coverage'
condition: succeededOrFailed()
- task: PublishCodeCoverageResults@1
condition: succeededOrFailed()
inputs:
codeCoverageTool: 'cobertura'
summaryFileLocation: ./src/Maths/Silk.NET.Maths.Tests/cobertura.xml
- script: bash <(curl -s https://codecov.io/bash)
displayName: 'Upload to codecov.io'
condition: succeededOrFailed()

- script: dotnet pack --configuration $(buildConfiguration) --version-suffix build$(Build.BuildId)
displayName: 'Build/Pack'
- publish: build/output_packages
Expand Down
12 changes: 12 additions & 0 deletions src/Maths/ConvertHelper/ConvertHelper.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Superpower" Version="2.3.0" />
</ItemGroup>

</Project>
89 changes: 89 additions & 0 deletions src/Maths/ConvertHelper/Parser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// This file is part of Silk.NET.
//
// You may modify and distribute Silk.NET under the terms
// of the MIT license. See the LICENSE file for details.

using Superpower;
using Superpower.Parsers;

namespace ConvertHelper
{
internal class ArithmeticExpressionParser
{
private static readonly TokenListParser<ArithmeticExpressionToken, ExpressionType> Add = Operator
(ArithmeticExpressionToken.Plus, ExpressionType.Add);

private static readonly TokenListParser<ArithmeticExpressionToken, ExpressionType> Subtract = Operator
(ArithmeticExpressionToken.Minus, ExpressionType.Subtract);

private static readonly TokenListParser<ArithmeticExpressionToken, ExpressionType> Multiply = Operator
(ArithmeticExpressionToken.Times, ExpressionType.Multiply);

private static readonly TokenListParser<ArithmeticExpressionToken, ExpressionType> Divide = Operator
(ArithmeticExpressionToken.Divide, ExpressionType.Divide);

private static readonly TokenListParser<ArithmeticExpressionToken, ExpressionType> Comma = Operator
(ArithmeticExpressionToken.Comma, ExpressionType.Comma);

private static readonly TokenListParser<ArithmeticExpressionToken, string> Variable = Token.EqualTo
(ArithmeticExpressionToken.None)
.Apply(Character.LetterOrDigit.Or(Character.EqualTo('.')).AtLeastOnce())
.Select(n => new string(n))
.Named("variable");

private static readonly TokenListParser<ArithmeticExpressionToken, string> Constant = Token.EqualTo
(ArithmeticExpressionToken.Number)
.Apply(Numerics.Decimal)
.Select(n => $"Scalar.As<T>({n.ToString()})")
.Named("constant")
.Or(Variable);

private static readonly TokenListParser<ArithmeticExpressionToken, string> Factor =
(from lparen in Token.EqualTo
(ArithmeticExpressionToken.LParen)
from expr in Parse.Ref(() => Expr)
from rparen in Token.EqualTo(ArithmeticExpressionToken.RParen)
select $"({expr})").Or(Constant);

private static readonly TokenListParser<ArithmeticExpressionToken, string> Operand = (from sign in Token.EqualTo
(ArithmeticExpressionToken.Minus)
from factor in Factor
select $"Scalar.Negate<T>({factor})").Or(Factor)
.Named("expression");

private static readonly TokenListParser<ArithmeticExpressionToken, string> Term = Parse.Chain
(Multiply.Or(Divide), Operand, AddParenthese);

private static readonly TokenListParser<ArithmeticExpressionToken, string> Expr = Parse.Chain
(Add.Or(Subtract), Term, AddParenthese);

private static readonly TokenListParser<ArithmeticExpressionToken, string> Exprs = Parse.Chain
(Comma, Expr, AddParenthese);

public static readonly TokenListParser<ArithmeticExpressionToken, string> Transform = Exprs.AtEnd();

private static TokenListParser<ArithmeticExpressionToken, ExpressionType> Operator
(ArithmeticExpressionToken op, ExpressionType opType)
=> Token.EqualTo(op).Value(opType);

private static string AddParenthese(ExpressionType type, string left, string right)
=> type switch
{
ExpressionType.Add => $"Scalar.Add<T>({left}, {right})",
ExpressionType.Subtract => $"Scalar.Subtract<T>({left}, {right})",
ExpressionType.Multiply => $"Scalar.Multiply<T>({left}, {right})",
ExpressionType.Divide => $"Scalar.Divide<T>({left}, {right})",
ExpressionType.Comma => $"({left}), ({right})",
_ => $"Scalar.???<T>({left}, {right})"
};

private enum ExpressionType
{
Add,
Subtract,
Multiply,
Divide,
Comma
}
}
}
54 changes: 54 additions & 0 deletions src/Maths/ConvertHelper/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// This file is part of Silk.NET.
//
// You may modify and distribute Silk.NET under the terms
// of the MIT license. See the LICENSE file for details.

using System;
using System.Text;

namespace ConvertHelper
{
internal class Program
{
private static void Main(string[] args)
{
Console.WriteLine
("Hey! I'll try to convert any normal C# expression to a Silk.NET.Maths.Scalar using expression!");
Console.WriteLine("Simply paste your code below, and I'll translate line-by-line. I'll try to keep `,`");
Console.WriteLine("A single empty line will start translation");

var tokenizer = new ArithmeticExpressionTokenizer();

while (true)
{
var sb = new StringBuilder();
while (true)
{
var i = Console.ReadLine();
if (string.IsNullOrWhiteSpace(i))
{
break;
}

sb.AppendLine(i);
}

var result = tokenizer.TryTokenize(sb.ToString());
if (!result.HasValue)
{
Console.WriteLine(result.FormatErrorMessageFragment());
continue;
}

var transform = ArithmeticExpressionParser.Transform(result.Value);
if (!transform.HasValue)
{
Console.WriteLine(result.FormatErrorMessageFragment());
continue;
}

Console.WriteLine(transform.Value);
}
}
}
}
34 changes: 34 additions & 0 deletions src/Maths/ConvertHelper/Token.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// This file is part of Silk.NET.
//
// You may modify and distribute Silk.NET under the terms
// of the MIT license. See the LICENSE file for details.

using Superpower.Display;

namespace ConvertHelper
{
internal enum ArithmeticExpressionToken
{
None,

Number,

[Token(Category = "operator", Example = "+")]
Plus,

[Token(Category = "operator", Example = "-")]
Minus,

[Token(Category = "operator", Example = "*")]
Times,

[Token(Category = "operator", Example = "-")]
Divide,

[Token(Example = "(")] LParen,

[Token(Example = ")")] RParen,

[Token(Example = ",")] Comma
}
}
78 changes: 78 additions & 0 deletions src/Maths/ConvertHelper/Tokenizer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// This file is part of Silk.NET.
//
// You may modify and distribute Silk.NET under the terms
// of the MIT license. See the LICENSE file for details.

using System.Collections.Generic;
using Superpower;
using Superpower.Model;
using Superpower.Parsers;

namespace ConvertHelper
{
internal class ArithmeticExpressionTokenizer : Tokenizer<ArithmeticExpressionToken>
{
private readonly Dictionary<char, ArithmeticExpressionToken> _operators =
new Dictionary<char, ArithmeticExpressionToken>
{
['+'] = ArithmeticExpressionToken.Plus,
['-'] = ArithmeticExpressionToken.Minus,
['*'] = ArithmeticExpressionToken.Times,
['/'] = ArithmeticExpressionToken.Divide,
['('] = ArithmeticExpressionToken.LParen,
[')'] = ArithmeticExpressionToken.RParen,
[','] = ArithmeticExpressionToken.Comma
};

protected override IEnumerable<Result<ArithmeticExpressionToken>> Tokenize(TextSpan span)
{
var next = SkipWhiteSpace(span);
if (!next.HasValue)
{
yield break;
}

do
{
ArithmeticExpressionToken charToken;

var ch = next.Value;
if (ch >= '0' && ch <= '9')
{
var integer = Numerics.Integer(next.Location);
next = integer.Remainder.ConsumeChar();
yield return Result.Value(ArithmeticExpressionToken.Number, integer.Location, integer.Remainder);
}
else if (_operators.TryGetValue(ch, out charToken))
{
yield return Result.Value(charToken, next.Location, next.Remainder);
next = next.Remainder.ConsumeChar();
}
else
{
var start = next.Location;
while (true)
{
var c = next.Location.ConsumeChar();
if (!c.HasValue || _operators.TryGetValue(c.Value, out _))
{
break;
}

if (char.IsWhiteSpace(c.Value))
{
break;
}

next = c.Remainder.ConsumeChar();
}

var location = start.Until(next.Location);
yield return Result.Value(ArithmeticExpressionToken.None, location, next.Location);
}

next = SkipWhiteSpace(next.Location);
} while (next.HasValue);
}
}
}
Loading