Skip to content

Commit

Permalink
Merge negitive fix
Browse files Browse the repository at this point in the history
  • Loading branch information
BLM16 committed May 3, 2022
2 parents 0f076c4 + e6b9ffa commit dc7be30
Show file tree
Hide file tree
Showing 12 changed files with 262 additions and 13 deletions.
15 changes: 15 additions & 0 deletions Calculator.Examples/Calculator.Examples.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,32 @@
<Version>2.0.0</Version>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageTags>calculator</PackageTags>
<PackageIcon>icon.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
<ApplicationIcon>icon.ico</ApplicationIcon>
</PropertyGroup>

<ItemGroup>
<Content Include="icon.ico" />
</ItemGroup>

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

<ItemGroup>
<None Include="..\icon.png">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
<None Include="..\LICENSE">
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>
<None Include="..\README.md">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>

</Project>
Binary file added Calculator.Examples/icon.ico
Binary file not shown.
2 changes: 1 addition & 1 deletion Calculator.Tests/Calculator.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<RepositoryUrl>https://github.com/BLM16/Tokenized-Calculator</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageProjectUrl />
<Version>2.0.0</Version>
<Version>4.1.0</Version>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageTags>calculator</PackageTags>
<RootNamespace>BLM16.Util.$(MSBuildProjectName.Replace(" ", "_"))</RootNamespace>
Expand Down
107 changes: 105 additions & 2 deletions Calculator.Tests/LexerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace BLM16.Util.Calculator.Tests;
[TestClass]
public class LexerTests
{
private readonly Lexer lexer = new(Calculator.DefaultOperatorList);
private readonly Lexer lexer = new(Calculator.BuiltinOperatorList);

/// <summary>
/// Checks that equations are correctly tokenized
Expand All @@ -34,8 +34,12 @@ public void Parse_ParsesEquation()
new Token(TokenType.OPERATOR, DefaultOperators.Division),
new Token(TokenType.LBRACK, '('),
new Token(TokenType.NUMBER, '5'),
new Token(TokenType.OPERATOR, DefaultOperators.Addition),
new Token(TokenType.LBRACK, '('),
new Token(TokenType.NUMBER, '0'),
new Token(TokenType.OPERATOR, DefaultOperators.Subtraction),
new Token(TokenType.NUMBER, '3'),
new Token(TokenType.RBRACK, ')'),
new Token(TokenType.RBRACK, ')')
};

Expand Down Expand Up @@ -90,7 +94,106 @@ public void Parse_ExceptionOnMalformedDoubles(string equation)
[DataTestMethod]
[ExpectedException(typeof(MathSyntaxException))]
[DataRow("157+*3.2/6")]
[DataRow("134+-8")]
[DataRow("134/+8")]
public void Parse_ExceptionOnConsecutiveOperators(string equation)
=> lexer.Parse(equation);

/// <summary>
/// Checks that subtraction signs are lexed into parenthesized binary expressions correctly
/// </summary>
/// <param name="equation">The equation to lex</param>
/// <param name="expected">The expected result</param>
[DataTestMethod]
[DynamicData(nameof(NegativeExpressions), DynamicDataSourceType.Property)]
public void Parse_WrapNegativeExpressions(string equation, List<Token> expected)
=> CollectionAssert.AreEqual(expected, lexer.Parse(equation));

/// <summary>
/// The equations and expected values to test subtraction and negatives with
/// </summary>
private static IEnumerable<object[]> NegativeExpressions
{
get
{
yield return new object[]
{
"8-4",
new List<Token>()
{
new Token(TokenType.NUMBER, '8'),
new Token(TokenType.OPERATOR, DefaultOperators.Addition),
new Token(TokenType.LBRACK, '('),
new Token(TokenType.NUMBER, '0'),
new Token(TokenType.OPERATOR, DefaultOperators.Subtraction),
new Token(TokenType.NUMBER, '4'),
new Token(TokenType.RBRACK, ')'),
}
};

yield return new object[]
{
"2-(17-5)",
new List<Token>()
{
new Token(TokenType.NUMBER, '2'),
new Token(TokenType.OPERATOR, DefaultOperators.Addition),
new Token(TokenType.LBRACK, '('),
new Token(TokenType.NUMBER, '0'),
new Token(TokenType.OPERATOR, DefaultOperators.Subtraction),
new Token(TokenType.LBRACK, '('),
new Token(TokenType.NUMBER, "17"),
new Token(TokenType.OPERATOR, DefaultOperators.Addition),
new Token(TokenType.LBRACK, '('),
new Token(TokenType.NUMBER, '0'),
new Token(TokenType.OPERATOR, DefaultOperators.Subtraction),
new Token(TokenType.NUMBER, '5'),
new Token(TokenType.RBRACK, ')'),
new Token(TokenType.RBRACK, ')'),
new Token(TokenType.RBRACK, ')')
}
};

yield return new object[]
{
"12/-3",
new List<Token>()
{
new Token(TokenType.NUMBER, "12"),
new Token(TokenType.OPERATOR, DefaultOperators.Division),
new Token(TokenType.LBRACK, '('),
new Token(TokenType.NUMBER, '0'),
new Token(TokenType.OPERATOR, DefaultOperators.Subtraction),
new Token(TokenType.NUMBER, '3'),
new Token(TokenType.RBRACK, ')')
}
};

yield return new object[]
{
"-6*(-3-5)",
new List<Token>()
{
new Token(TokenType.LBRACK, '('),
new Token(TokenType.NUMBER, '0'),
new Token(TokenType.OPERATOR, DefaultOperators.Subtraction),
new Token(TokenType.NUMBER, '6'),
new Token(TokenType.OPERATOR, DefaultOperators.Multiplication),
new Token(TokenType.LBRACK, '('),
new Token(TokenType.LBRACK, '('),
new Token(TokenType.NUMBER, '0'),
new Token(TokenType.OPERATOR, DefaultOperators.Subtraction),
new Token(TokenType.NUMBER, '3'),
new Token(TokenType.OPERATOR, DefaultOperators.Addition),
new Token(TokenType.LBRACK, '('),
new Token(TokenType.NUMBER, '0'),
new Token(TokenType.OPERATOR, DefaultOperators.Subtraction),
new Token(TokenType.NUMBER, '5'),
new Token(TokenType.RBRACK, ')'),
new Token(TokenType.RBRACK, ')'),
new Token(TokenType.RBRACK, ')'),
new Token(TokenType.RBRACK, ')')
}
};
}
}
}
63 changes: 62 additions & 1 deletion Calculator.Tests/ParserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,17 @@ public void Evaluate_EvaluatesEquation(List<Token> tokens, double expected)
=> Assert.AreEqual(expected, parser.Evaluate(tokens));

/// <summary>
/// The lexed equations and expected values to test with
/// Checks that malformed binary operations throw an exception
/// </summary>
/// <param name="tokens">The tokenized equation to test</param>
[DataTestMethod]
[DynamicData(nameof(MalformedBinaryOperatorEquations), DynamicDataSourceType.Property)]
[ExpectedException(typeof(MathSyntaxException))]
public void Evaluate_ExceptionOnMalformedBinaryOperators(List<Token> tokens)
=> parser.Evaluate(tokens);

/// <summary>
/// The lexed equations and expected values to test correct evaluation with
/// </summary>
public static IEnumerable<object[]> LexedEquations
{
Expand Down Expand Up @@ -146,4 +156,55 @@ public static IEnumerable<object[]> LexedEquations
};
}
}

/// <summary>
/// The lexed equations to test malformed binary operations
/// </summary>
public static IEnumerable<object[]> MalformedBinaryOperatorEquations
{
get
{
yield return new[]{
new List<Token>()
{
new Token(TokenType.OPERATOR, DefaultOperators.Addition)
}
};

yield return new[]{
new List<Token>()
{
new Token(TokenType.NUMBER, '5'),
new Token(TokenType.OPERATOR, DefaultOperators.Addition)
}
};

yield return new[]{
new List<Token>()
{
new Token(TokenType.NUMBER, '5'),
new Token(TokenType.OPERATOR, DefaultOperators.Addition),
new Token(TokenType.OPERATOR, DefaultOperators.Division)
}
};

yield return new[]{
new List<Token>()
{
new Token(TokenType.NUMBER, '5'),
new Token(TokenType.LBRACK, '('),
new Token(TokenType.OPERATOR, DefaultOperators.Division)
}
};

yield return new[]{
new List<Token>()
{
new Token(TokenType.NUMBER, '5'),
new Token(TokenType.OPERATOR, DefaultOperators.Division),
new Token(TokenType.RBRACK, ')')
}
};
}
}
}
13 changes: 12 additions & 1 deletion Calculator.Tests/StandardizerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace BLM16.Util.Calculator.Tests;
[TestClass]
public class StandardizerTests
{
private readonly Standardizer standardizer = new(Calculator.DefaultOperatorList, Calculator.DefaultConstantList, Calculator.DefaultFunctionList);
private readonly Standardizer standardizer = new(Calculator.BuiltinOperatorList, Calculator.DefaultConstantList, Calculator.DefaultFunctionList);

/// <summary>
/// Checks that whitespace is correctly removed from the equations
Expand Down Expand Up @@ -49,6 +49,17 @@ public void FixBrackets_ExceptionOnTooManyClosingBrackets(string equation)

#endregion

/// <summary>
/// Checks that repeating operators are simplified where possible
/// </summary>
/// <param name="equation">The equation to standardize</param>
/// <param name="expected">The expected output</param>
[DataTestMethod]
[DataRow("3+-5", "3-5")]
[DataRow("22--5", "22+5")]
public void FixRepeatingOperators(string equation, string expected)
=> Assert.AreEqual(expected, standardizer.Standardize(equation));

/// <summary>
/// Checks that functions are correctly evaluated
/// </summary>
Expand Down
10 changes: 7 additions & 3 deletions Calculator/Calculator.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using BLM16.Util.Calculator.Models;
using System.Collections.Generic;

[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Calculator.Tests")]
namespace BLM16.Util.Calculator;
Expand Down Expand Up @@ -34,13 +35,16 @@ public class Calculator
/// </summary>
private readonly Function[] functions;

/// <param name="operators">The list of operators the calculator recognizes. Defaults to <see cref="DefaultOperatorList"/> if no value is provided.</param>
/// <param name="operators">The list of operators the calculator recognizes. Always includes <see cref="BuiltinOperatorList"/>.</param>
/// <param name="constants">The list of constants the calculator recognizes. Defaults to <see cref="DefaultConstantList"/> if no value is provided.</param>
/// <param name="functions">The list of functions the calculator recognizes. Defaults to <see cref="DefaultFunctionList"/> if no value is provided.</param>
public Calculator(Operator[] operators = null, Constant[] constants = null, Function[] functions = null)
{
var _ops = new List<Operator>(BuiltinOperatorList); // Add default operators
_ops.AddRange(operators ?? System.Array.Empty<Operator>()); // Add provided operators if any

// Use the default operators if no operators are provided
this.operators = operators ?? DefaultOperatorList;
this.operators = _ops.ToArray();
// Use the default constants if no constants are provided
this.constants = constants ?? DefaultConstantList;
// Use the default functions if no functions are provided
Expand Down Expand Up @@ -71,7 +75,7 @@ public double Calculate(string equation)
/// <summary>
/// A list of the default operators used by the calculator
/// </summary>
public static Operator[] DefaultOperatorList => new Operator[]
public static Operator[] BuiltinOperatorList => new Operator[]
{
DefaultOperators.Addition,
DefaultOperators.Subtraction,
Expand Down
7 changes: 6 additions & 1 deletion Calculator/Calculator.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<Copyright>Copyright © 2021 Bradley Myers. All rights reserved.</Copyright>
<RepositoryUrl>https://github.com/BLM16/Tokenized-Calculator</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<Version>4.0.1</Version>
<Version>4.1.0</Version>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageTags>calculator; math; solve</PackageTags>
<PackageId>BLM16.Util.$(AssemblyName)</PackageId>
Expand All @@ -18,8 +18,13 @@
<NeutralLanguage>en-CA</NeutralLanguage>
<RootNamespace>BLM16.Util.$(MSBuildProjectName.Replace(" ", "_"))</RootNamespace>
<PackageIcon>icon.png</PackageIcon>
<ApplicationIcon>icon.ico</ApplicationIcon>
</PropertyGroup>

<ItemGroup>
<Content Include="icon.ico" />
</ItemGroup>

<ItemGroup>
<None Include="..\icon.png">
<Pack>True</Pack>
Expand Down
31 changes: 29 additions & 2 deletions Calculator/Lexer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public Lexer(Operator[] operators)
public List<Token> Parse(string equation)
{
var tokens = new List<Token>();
var negCount = 0;

// Loop through each char and convert to tokens
for (int i = 0; i < equation.Length; i++)
Expand Down Expand Up @@ -80,7 +81,20 @@ public List<Token> Parse(string equation)
{
throw new MathSyntaxException("Malformed expression: operator requires numbers to operate on");
}
if (tokens.LastOrDefault().Type == TokenType.OPERATOR)

if (c == '-')
{
negCount++;
if (tokens.Count > 0 && tokens.LastOrDefault().Type != TokenType.OPERATOR && tokens.LastOrDefault().Type != TokenType.LBRACK)
{
tokens.Add(new Token(TokenType.OPERATOR, DefaultOperators.Addition));
}
tokens.Add(new Token(TokenType.LBRACK, '('));
tokens.Add(new Token(TokenType.NUMBER, '0'));
tokens.Add(new Token(TokenType.OPERATOR, DefaultOperators.Subtraction));
continue;
}
else if (tokens.LastOrDefault().Type == TokenType.OPERATOR)
{
throw new MathSyntaxException("Consecutive operators found");
}
Expand All @@ -92,7 +106,13 @@ public List<Token> Parse(string equation)

if (op.Any())
{
tokens.Add(new Token(TokenType.OPERATOR, op.First()));
var cur_op = op.First();
if (negCount > 0 && !(cur_op > DefaultOperators.Subtraction))
{
tokens.Add(new Token(TokenType.RBRACK, ')'));
negCount--;
}
tokens.Add(new Token(TokenType.OPERATOR, cur_op));
}
else
{
Expand All @@ -102,6 +122,13 @@ public List<Token> Parse(string equation)
}
}

// If negative was the last operator
while (negCount > 0)
{
tokens.Add(new Token(TokenType.RBRACK, ')'));
negCount--;
}

return tokens;
}
}
Loading

0 comments on commit dc7be30

Please sign in to comment.