Skip to content

Commit

Permalink
Merge pull request #10 from miroiu/master
Browse files Browse the repository at this point in the history
Merge into release branch
  • Loading branch information
miroiu authored Jan 22, 2024
2 parents 2dfc129 + f82c970 commit f55ccaa
Show file tree
Hide file tree
Showing 30 changed files with 455 additions and 251 deletions.
4 changes: 4 additions & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# These are supported funding model platforms

github: miroiu
custom: ["https://www.buymeacoffee.com/miroiu", "https://paypal.me/miroiuemanuel"]
1 change: 1 addition & 0 deletions .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ jobs:
3.1.x
5.0.x
6.0.x
8.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
- name: Setup .NET Core
uses: actions/setup-dotnet@v3.0.1
with:
dotnet-version: '6.0.x'
dotnet-version: '8.0.x'
- name: Install dependencies
run: dotnet restore
- name: Build
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
> V3 README can be found here: <https://github.com/miroiu/string-math/tree/release-3.0.0>
> **NEW!** Boolean math example: https://github.com/miroiu/string-math/pull/6/files
# String Math [![NuGet](https://img.shields.io/nuget/v/StringMath?style=flat-square&logo=nuget)](https://www.nuget.org/packages/StringMath/) [![Downloads](https://img.shields.io/nuget/dt/StringMath?label=downloads&style=flat-square&logo=nuget)](https://www.nuget.org/packages/StringMath) ![.NET](https://img.shields.io/static/v1?label=%20&message=Framework%204.6.1%20to%20NET%206&color=5C2D91&style=flat-square&logo=.net) ![](https://img.shields.io/static/v1?label=%20&message=documentation&color=yellow&style=flat-square)

Calculates the value of a math expression from a string returning a double.
Expand Down
45 changes: 45 additions & 0 deletions StringMath.Benchmarks/Benchmarks.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;

namespace StringMath.Benchmarks
{
[MemoryDiagnoser(true)]
[SimpleJob(RuntimeMoniker.Net80, warmupCount: 0, iterationCount: 1, launchCount: 1)]
public class Benchmarks
{
[Benchmark]
public void Tokenize()
{
var tokenizer = new Tokenizer("1.23235456576878798 - ((3 + {b}) max .1) ^ sqrt(-999 / 2 * 3 max 5) + !5 - 0.00000000002 / {ahghghh}");

Token token;

do
{
token = tokenizer.ReadToken();
}
while (token.Type != TokenType.EndOfCode);
}

[Benchmark]
public void Parse()
{
var tokenizer = new Tokenizer("1.23235456576878798 - ((3 + {b}) max .1) ^ sqrt(-999 / 2 * 3 max 5) + !5 - 0.00000000002 / {ahghghh}");
var parser = new Parser(tokenizer, MathContext.Default);
_ = parser.Parse();
}

[Benchmark]
public double Evaluate()
{
return "1.23235456576878798 - ((3 + {b}) max .1) ^ sqrt(-999 / 2 * 3 max 5) + !5 - 0.00000000002 / {ahghghh}".ToMathExpr().Substitute("b", 12989d).Substitute("ahghghh", 12345d).Result;
}

[Benchmark]
public double Compile()
{
var fn = "1.23235456576878798 - ((3 + {b}) max .1) ^ sqrt(-999 / 2 * 3 max 5) + !5 - 0.00000000002 / {ahghghh}".ToMathExpr().Substitute("b", 12989d).Substitute("ahghghh", 12345d).Compile("ahghghh");
return fn(12345d);
}
}
}
4 changes: 4 additions & 0 deletions StringMath.Benchmarks/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
using BenchmarkDotNet.Running;
using StringMath.Benchmarks;

BenchmarkRunner.Run<Benchmarks>();
21 changes: 21 additions & 0 deletions StringMath.Benchmarks/StringMath.Benchmarks.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Optimize>true</Optimize>
<SignAssembly>true</SignAssembly>

<AssemblyOriginatorKeyFile>../build/string-math.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.12" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\StringMath\StringMath.csproj" />
</ItemGroup>
</Project>
81 changes: 81 additions & 0 deletions StringMath.Tests/BooleanExprTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using NUnit.Framework;
using System;

namespace StringMath.Tests
{
[TestFixture]
internal class BooleanExprTests
{
private MathContext _context;

[SetUp]
public void Setup()
{
MathExpr.AddVariable("true", 1);
MathExpr.AddVariable("false", 0);

_context = new MathContext();
_context.RegisterLogical("and", (a, b) => a && b, Precedence.Multiplication);
_context.RegisterLogical("or", (a, b) => a || b, Precedence.Addition);
_context.RegisterLogical(">", (a, b) => a > b, Precedence.Power);
_context.RegisterLogical("<", (a, b) => a < b, Precedence.Power);
_context.RegisterLogical("!", (a) => !a);
}

[Test]
public void Evaluate_Variable_Substitution()
{
MathExpr expr = new MathExpr("{a} and 1", _context);
Assert.IsFalse(expr.Substitute("a", false).EvalBoolean());
Assert.IsTrue(expr.Substitute("a", true).EvalBoolean());
}

[Test]
public void Evaluate_Boolean_Numbers()
{
bool expr = "1 and 1".EvalBoolean(_context);
Assert.IsTrue(expr);

bool result = "1 and 0 or !0 and 3 > 2".EvalBoolean(_context);
Assert.IsTrue(result);
}

[Test]
public void Evaluate_Globals_Variables()
{
Assert.IsTrue("{true} or {false} and {true}".EvalBoolean(_context));
Assert.IsTrue("{true} or {false}".EvalBoolean(_context));
Assert.IsFalse("{false} or {false}".EvalBoolean(_context));
Assert.IsFalse("{true} and {false}".EvalBoolean(_context));
Assert.IsTrue("{true} and {true}".EvalBoolean(_context));
}

[Test]
public void Evaluate_Binary_Operation()
{
_context.RegisterBinary("+", (a, b) => a + b);
Assert.IsTrue("(3 + 5) > 7".EvalBoolean(_context));
}
}

static class BooleanMathExtensions
{
public static bool EvalBoolean(this string value) => value.ToMathExpr().EvalBoolean();

public static bool EvalBoolean(this string value, IMathContext context) => value.ToMathExpr(context).EvalBoolean();

public static bool EvalBoolean(this MathExpr value) => value.Result != 0;

public static MathExpr Substitute(this MathExpr expr, string name, bool value)
=> expr.Substitute(name, value is true ? 1 : 0);

public static void RegisterLogical(this IMathContext context, string operatorName, Func<bool, bool, bool> operation, Precedence? precedence)
=> context.RegisterBinary(operatorName, (a, b) => operation(a != 0, b != 0) ? 1 : 0, precedence);

public static void RegisterLogical(this IMathContext context, string operatorName, Func<double, double, bool> operation, Precedence? precedence)
=> context.RegisterBinary(operatorName, (a, b) => operation(a, b) ? 1 : 0, precedence);

public static void RegisterLogical(this IMathContext context, string operatorName, Func<bool, bool> operation)
=> context.RegisterUnary(operatorName, (a) => operation(a != 0) ? 1 : 0);
}
}
2 changes: 1 addition & 1 deletion StringMath.Tests/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ static class Extensions
{
public static List<Token> ReadAllTokens(this string input)
{
ITokenizer tokenizer = new Tokenizer(input);
Tokenizer tokenizer = new Tokenizer(input);
List<Token> tokens = new List<Token>();

Token t;
Expand Down
59 changes: 54 additions & 5 deletions StringMath.Tests/MathExprTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -293,9 +293,9 @@ public void Compile(string input, double expected)

[Test]
[TestCase("1 + {a}", new[] { "a" }, new[] { 1d }, 2)]
public void Compile1(string input, string[] paramsOrder, double[] paramsValues, double expected)
public void Compile_1Variable(string input, string[] paramsOrder, double[] paramsValues, double expected)
{
Func<double, double> fn = input.ToMathExpr().Compile(paramsOrder[0]);
var fn = input.ToMathExpr().Compile(paramsOrder[0]);
double result = fn(paramsValues[0]);

Assert.AreEqual(expected, result);
Expand All @@ -304,20 +304,69 @@ public void Compile1(string input, string[] paramsOrder, double[] paramsValues,
[Test]
[TestCase("(1 + {a}) * {b}", new[] { "b", "a" }, new[] { 2d, 3d }, 8)]
[TestCase("(1 + {b}) * {a}", new[] { "b", "a" }, new[] { 2d, 3d }, 9)]
public void Compile2(string input, string[] paramsOrder, double[] paramsValues, double expected)
public void Compile_2Variables(string input, string[] paramsOrder, double[] paramsValues, double expected)
{
Func<double, double, double> fn = input.ToMathExpr().Compile(paramsOrder[0], paramsOrder[1]);
var fn = input.ToMathExpr().Compile(paramsOrder[0], paramsOrder[1]);
double result = fn(paramsValues[0], paramsValues[1]);

Assert.AreEqual(expected, result);
}

[Test]
public void Compile1_Throws_Missing_Variable()
[TestCase("({c} - 1 + {a}) * {b}", new[] { "b", "a", "c" }, new[] { 2d, 3d, 2d }, 8)]
[TestCase("({c} - 1 + {b}) * {a}", new[] { "b", "a", "c" }, new[] { 2d, 3d, 2d }, 9)]
public void Compile_3Variables(string input, string[] paramsOrder, double[] paramsValues, double expected)
{
var fn = input.ToMathExpr().Compile(paramsOrder[0], paramsOrder[1], paramsOrder[2]);
double result = fn(paramsValues[0], paramsValues[1], paramsValues[2]);

Assert.AreEqual(expected, result);
}

[Test]
public void Compile_Throws_Missing_Variable()
{
MathException ex = Assert.Throws<MathException>(() => "1 + {a}".ToMathExpr().Compile("b"));

Assert.AreEqual(MathException.ErrorCode.UNEXISTING_VARIABLE, ex.Code);
}

[Test]
public void Compile_Throws_Missing_Variable_When_No_Parameter_Provided()
{
MathException ex = Assert.Throws<MathException>(() => "1 + {a}".ToMathExpr().Compile());

Assert.AreEqual(MathException.ErrorCode.UNEXISTING_VARIABLE, ex.Code);
}

[Test]
public void Compile_Resolves_Remaining_Variables()
{
var expr = "1 + {a}".ToMathExpr().Substitute("a", 3);
var fn = expr.Compile();
double result = fn();

Assert.AreEqual(4, result);
}

[Test]
public void Compile_Resolves_Remaining_Variables2()
{
var expr = "1 + {a} * {b}".ToMathExpr().Substitute("a", 3);
var fn = expr.Compile("b");
double result = fn(2);

Assert.AreEqual(7, result);
}

[Test]
public void Compile_Resolves_Global_Variables()
{
var expr = "1 + {PI}".ToMathExpr();
var fn = expr.Compile();
double result = fn();

Assert.AreEqual(1 + Math.PI, result);
}
}
}
Loading

0 comments on commit f55ccaa

Please sign in to comment.