Skip to content

Commit dc7be30

Browse files
committed
Merge negitive fix
2 parents 0f076c4 + e6b9ffa commit dc7be30

File tree

12 files changed

+262
-13
lines changed

12 files changed

+262
-13
lines changed

Calculator.Examples/Calculator.Examples.csproj

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,32 @@
1313
<Version>2.0.0</Version>
1414
<PackageLicenseFile>LICENSE</PackageLicenseFile>
1515
<PackageTags>calculator</PackageTags>
16+
<PackageIcon>icon.png</PackageIcon>
17+
<PackageReadmeFile>README.md</PackageReadmeFile>
18+
<ApplicationIcon>icon.ico</ApplicationIcon>
1619
</PropertyGroup>
1720

21+
<ItemGroup>
22+
<Content Include="icon.ico" />
23+
</ItemGroup>
24+
1825
<ItemGroup>
1926
<ProjectReference Include="..\Calculator\Calculator.csproj" />
2027
</ItemGroup>
2128

2229
<ItemGroup>
30+
<None Include="..\icon.png">
31+
<Pack>True</Pack>
32+
<PackagePath>\</PackagePath>
33+
</None>
2334
<None Include="..\LICENSE">
2435
<Pack>True</Pack>
2536
<PackagePath></PackagePath>
2637
</None>
38+
<None Include="..\README.md">
39+
<Pack>True</Pack>
40+
<PackagePath>\</PackagePath>
41+
</None>
2742
</ItemGroup>
2843

2944
</Project>

Calculator.Examples/icon.ico

66.1 KB
Binary file not shown.

Calculator.Tests/Calculator.Tests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
<RepositoryUrl>https://github.com/BLM16/Tokenized-Calculator</RepositoryUrl>
1111
<RepositoryType>git</RepositoryType>
1212
<PackageProjectUrl />
13-
<Version>2.0.0</Version>
13+
<Version>4.1.0</Version>
1414
<PackageLicenseFile>LICENSE</PackageLicenseFile>
1515
<PackageTags>calculator</PackageTags>
1616
<RootNamespace>BLM16.Util.$(MSBuildProjectName.Replace(" ", "_"))</RootNamespace>

Calculator.Tests/LexerTests.cs

Lines changed: 105 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace BLM16.Util.Calculator.Tests;
1010
[TestClass]
1111
public class LexerTests
1212
{
13-
private readonly Lexer lexer = new(Calculator.DefaultOperatorList);
13+
private readonly Lexer lexer = new(Calculator.BuiltinOperatorList);
1414

1515
/// <summary>
1616
/// Checks that equations are correctly tokenized
@@ -34,8 +34,12 @@ public void Parse_ParsesEquation()
3434
new Token(TokenType.OPERATOR, DefaultOperators.Division),
3535
new Token(TokenType.LBRACK, '('),
3636
new Token(TokenType.NUMBER, '5'),
37+
new Token(TokenType.OPERATOR, DefaultOperators.Addition),
38+
new Token(TokenType.LBRACK, '('),
39+
new Token(TokenType.NUMBER, '0'),
3740
new Token(TokenType.OPERATOR, DefaultOperators.Subtraction),
3841
new Token(TokenType.NUMBER, '3'),
42+
new Token(TokenType.RBRACK, ')'),
3943
new Token(TokenType.RBRACK, ')')
4044
};
4145

@@ -90,7 +94,106 @@ public void Parse_ExceptionOnMalformedDoubles(string equation)
9094
[DataTestMethod]
9195
[ExpectedException(typeof(MathSyntaxException))]
9296
[DataRow("157+*3.2/6")]
93-
[DataRow("134+-8")]
97+
[DataRow("134/+8")]
9498
public void Parse_ExceptionOnConsecutiveOperators(string equation)
9599
=> lexer.Parse(equation);
100+
101+
/// <summary>
102+
/// Checks that subtraction signs are lexed into parenthesized binary expressions correctly
103+
/// </summary>
104+
/// <param name="equation">The equation to lex</param>
105+
/// <param name="expected">The expected result</param>
106+
[DataTestMethod]
107+
[DynamicData(nameof(NegativeExpressions), DynamicDataSourceType.Property)]
108+
public void Parse_WrapNegativeExpressions(string equation, List<Token> expected)
109+
=> CollectionAssert.AreEqual(expected, lexer.Parse(equation));
110+
111+
/// <summary>
112+
/// The equations and expected values to test subtraction and negatives with
113+
/// </summary>
114+
private static IEnumerable<object[]> NegativeExpressions
115+
{
116+
get
117+
{
118+
yield return new object[]
119+
{
120+
"8-4",
121+
new List<Token>()
122+
{
123+
new Token(TokenType.NUMBER, '8'),
124+
new Token(TokenType.OPERATOR, DefaultOperators.Addition),
125+
new Token(TokenType.LBRACK, '('),
126+
new Token(TokenType.NUMBER, '0'),
127+
new Token(TokenType.OPERATOR, DefaultOperators.Subtraction),
128+
new Token(TokenType.NUMBER, '4'),
129+
new Token(TokenType.RBRACK, ')'),
130+
}
131+
};
132+
133+
yield return new object[]
134+
{
135+
"2-(17-5)",
136+
new List<Token>()
137+
{
138+
new Token(TokenType.NUMBER, '2'),
139+
new Token(TokenType.OPERATOR, DefaultOperators.Addition),
140+
new Token(TokenType.LBRACK, '('),
141+
new Token(TokenType.NUMBER, '0'),
142+
new Token(TokenType.OPERATOR, DefaultOperators.Subtraction),
143+
new Token(TokenType.LBRACK, '('),
144+
new Token(TokenType.NUMBER, "17"),
145+
new Token(TokenType.OPERATOR, DefaultOperators.Addition),
146+
new Token(TokenType.LBRACK, '('),
147+
new Token(TokenType.NUMBER, '0'),
148+
new Token(TokenType.OPERATOR, DefaultOperators.Subtraction),
149+
new Token(TokenType.NUMBER, '5'),
150+
new Token(TokenType.RBRACK, ')'),
151+
new Token(TokenType.RBRACK, ')'),
152+
new Token(TokenType.RBRACK, ')')
153+
}
154+
};
155+
156+
yield return new object[]
157+
{
158+
"12/-3",
159+
new List<Token>()
160+
{
161+
new Token(TokenType.NUMBER, "12"),
162+
new Token(TokenType.OPERATOR, DefaultOperators.Division),
163+
new Token(TokenType.LBRACK, '('),
164+
new Token(TokenType.NUMBER, '0'),
165+
new Token(TokenType.OPERATOR, DefaultOperators.Subtraction),
166+
new Token(TokenType.NUMBER, '3'),
167+
new Token(TokenType.RBRACK, ')')
168+
}
169+
};
170+
171+
yield return new object[]
172+
{
173+
"-6*(-3-5)",
174+
new List<Token>()
175+
{
176+
new Token(TokenType.LBRACK, '('),
177+
new Token(TokenType.NUMBER, '0'),
178+
new Token(TokenType.OPERATOR, DefaultOperators.Subtraction),
179+
new Token(TokenType.NUMBER, '6'),
180+
new Token(TokenType.OPERATOR, DefaultOperators.Multiplication),
181+
new Token(TokenType.LBRACK, '('),
182+
new Token(TokenType.LBRACK, '('),
183+
new Token(TokenType.NUMBER, '0'),
184+
new Token(TokenType.OPERATOR, DefaultOperators.Subtraction),
185+
new Token(TokenType.NUMBER, '3'),
186+
new Token(TokenType.OPERATOR, DefaultOperators.Addition),
187+
new Token(TokenType.LBRACK, '('),
188+
new Token(TokenType.NUMBER, '0'),
189+
new Token(TokenType.OPERATOR, DefaultOperators.Subtraction),
190+
new Token(TokenType.NUMBER, '5'),
191+
new Token(TokenType.RBRACK, ')'),
192+
new Token(TokenType.RBRACK, ')'),
193+
new Token(TokenType.RBRACK, ')'),
194+
new Token(TokenType.RBRACK, ')')
195+
}
196+
};
197+
}
198+
}
96199
}

Calculator.Tests/ParserTests.cs

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,17 @@ public void Evaluate_EvaluatesEquation(List<Token> tokens, double expected)
2323
=> Assert.AreEqual(expected, parser.Evaluate(tokens));
2424

2525
/// <summary>
26-
/// The lexed equations and expected values to test with
26+
/// Checks that malformed binary operations throw an exception
27+
/// </summary>
28+
/// <param name="tokens">The tokenized equation to test</param>
29+
[DataTestMethod]
30+
[DynamicData(nameof(MalformedBinaryOperatorEquations), DynamicDataSourceType.Property)]
31+
[ExpectedException(typeof(MathSyntaxException))]
32+
public void Evaluate_ExceptionOnMalformedBinaryOperators(List<Token> tokens)
33+
=> parser.Evaluate(tokens);
34+
35+
/// <summary>
36+
/// The lexed equations and expected values to test correct evaluation with
2737
/// </summary>
2838
public static IEnumerable<object[]> LexedEquations
2939
{
@@ -146,4 +156,55 @@ public static IEnumerable<object[]> LexedEquations
146156
};
147157
}
148158
}
159+
160+
/// <summary>
161+
/// The lexed equations to test malformed binary operations
162+
/// </summary>
163+
public static IEnumerable<object[]> MalformedBinaryOperatorEquations
164+
{
165+
get
166+
{
167+
yield return new[]{
168+
new List<Token>()
169+
{
170+
new Token(TokenType.OPERATOR, DefaultOperators.Addition)
171+
}
172+
};
173+
174+
yield return new[]{
175+
new List<Token>()
176+
{
177+
new Token(TokenType.NUMBER, '5'),
178+
new Token(TokenType.OPERATOR, DefaultOperators.Addition)
179+
}
180+
};
181+
182+
yield return new[]{
183+
new List<Token>()
184+
{
185+
new Token(TokenType.NUMBER, '5'),
186+
new Token(TokenType.OPERATOR, DefaultOperators.Addition),
187+
new Token(TokenType.OPERATOR, DefaultOperators.Division)
188+
}
189+
};
190+
191+
yield return new[]{
192+
new List<Token>()
193+
{
194+
new Token(TokenType.NUMBER, '5'),
195+
new Token(TokenType.LBRACK, '('),
196+
new Token(TokenType.OPERATOR, DefaultOperators.Division)
197+
}
198+
};
199+
200+
yield return new[]{
201+
new List<Token>()
202+
{
203+
new Token(TokenType.NUMBER, '5'),
204+
new Token(TokenType.OPERATOR, DefaultOperators.Division),
205+
new Token(TokenType.RBRACK, ')')
206+
}
207+
};
208+
}
209+
}
149210
}

Calculator.Tests/StandardizerTests.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace BLM16.Util.Calculator.Tests;
88
[TestClass]
99
public class StandardizerTests
1010
{
11-
private readonly Standardizer standardizer = new(Calculator.DefaultOperatorList, Calculator.DefaultConstantList, Calculator.DefaultFunctionList);
11+
private readonly Standardizer standardizer = new(Calculator.BuiltinOperatorList, Calculator.DefaultConstantList, Calculator.DefaultFunctionList);
1212

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

5050
#endregion
5151

52+
/// <summary>
53+
/// Checks that repeating operators are simplified where possible
54+
/// </summary>
55+
/// <param name="equation">The equation to standardize</param>
56+
/// <param name="expected">The expected output</param>
57+
[DataTestMethod]
58+
[DataRow("3+-5", "3-5")]
59+
[DataRow("22--5", "22+5")]
60+
public void FixRepeatingOperators(string equation, string expected)
61+
=> Assert.AreEqual(expected, standardizer.Standardize(equation));
62+
5263
/// <summary>
5364
/// Checks that functions are correctly evaluated
5465
/// </summary>

Calculator/Calculator.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using BLM16.Util.Calculator.Models;
2+
using System.Collections.Generic;
23

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

37-
/// <param name="operators">The list of operators the calculator recognizes. Defaults to <see cref="DefaultOperatorList"/> if no value is provided.</param>
38+
/// <param name="operators">The list of operators the calculator recognizes. Always includes <see cref="BuiltinOperatorList"/>.</param>
3839
/// <param name="constants">The list of constants the calculator recognizes. Defaults to <see cref="DefaultConstantList"/> if no value is provided.</param>
3940
/// <param name="functions">The list of functions the calculator recognizes. Defaults to <see cref="DefaultFunctionList"/> if no value is provided.</param>
4041
public Calculator(Operator[] operators = null, Constant[] constants = null, Function[] functions = null)
4142
{
43+
var _ops = new List<Operator>(BuiltinOperatorList); // Add default operators
44+
_ops.AddRange(operators ?? System.Array.Empty<Operator>()); // Add provided operators if any
45+
4246
// Use the default operators if no operators are provided
43-
this.operators = operators ?? DefaultOperatorList;
47+
this.operators = _ops.ToArray();
4448
// Use the default constants if no constants are provided
4549
this.constants = constants ?? DefaultConstantList;
4650
// Use the default functions if no functions are provided
@@ -71,7 +75,7 @@ public double Calculate(string equation)
7175
/// <summary>
7276
/// A list of the default operators used by the calculator
7377
/// </summary>
74-
public static Operator[] DefaultOperatorList => new Operator[]
78+
public static Operator[] BuiltinOperatorList => new Operator[]
7579
{
7680
DefaultOperators.Addition,
7781
DefaultOperators.Subtraction,

Calculator/Calculator.csproj

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<Copyright>Copyright © 2021 Bradley Myers. All rights reserved.</Copyright>
99
<RepositoryUrl>https://github.com/BLM16/Tokenized-Calculator</RepositoryUrl>
1010
<RepositoryType>git</RepositoryType>
11-
<Version>4.0.1</Version>
11+
<Version>4.1.0</Version>
1212
<PackageLicenseFile>LICENSE</PackageLicenseFile>
1313
<PackageTags>calculator; math; solve</PackageTags>
1414
<PackageId>BLM16.Util.$(AssemblyName)</PackageId>
@@ -18,8 +18,13 @@
1818
<NeutralLanguage>en-CA</NeutralLanguage>
1919
<RootNamespace>BLM16.Util.$(MSBuildProjectName.Replace(" ", "_"))</RootNamespace>
2020
<PackageIcon>icon.png</PackageIcon>
21+
<ApplicationIcon>icon.ico</ApplicationIcon>
2122
</PropertyGroup>
2223

24+
<ItemGroup>
25+
<Content Include="icon.ico" />
26+
</ItemGroup>
27+
2328
<ItemGroup>
2429
<None Include="..\icon.png">
2530
<Pack>True</Pack>

Calculator/Lexer.cs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public Lexer(Operator[] operators)
2828
public List<Token> Parse(string equation)
2929
{
3030
var tokens = new List<Token>();
31+
var negCount = 0;
3132

3233
// Loop through each char and convert to tokens
3334
for (int i = 0; i < equation.Length; i++)
@@ -80,7 +81,20 @@ public List<Token> Parse(string equation)
8081
{
8182
throw new MathSyntaxException("Malformed expression: operator requires numbers to operate on");
8283
}
83-
if (tokens.LastOrDefault().Type == TokenType.OPERATOR)
84+
85+
if (c == '-')
86+
{
87+
negCount++;
88+
if (tokens.Count > 0 && tokens.LastOrDefault().Type != TokenType.OPERATOR && tokens.LastOrDefault().Type != TokenType.LBRACK)
89+
{
90+
tokens.Add(new Token(TokenType.OPERATOR, DefaultOperators.Addition));
91+
}
92+
tokens.Add(new Token(TokenType.LBRACK, '('));
93+
tokens.Add(new Token(TokenType.NUMBER, '0'));
94+
tokens.Add(new Token(TokenType.OPERATOR, DefaultOperators.Subtraction));
95+
continue;
96+
}
97+
else if (tokens.LastOrDefault().Type == TokenType.OPERATOR)
8498
{
8599
throw new MathSyntaxException("Consecutive operators found");
86100
}
@@ -92,7 +106,13 @@ public List<Token> Parse(string equation)
92106

93107
if (op.Any())
94108
{
95-
tokens.Add(new Token(TokenType.OPERATOR, op.First()));
109+
var cur_op = op.First();
110+
if (negCount > 0 && !(cur_op > DefaultOperators.Subtraction))
111+
{
112+
tokens.Add(new Token(TokenType.RBRACK, ')'));
113+
negCount--;
114+
}
115+
tokens.Add(new Token(TokenType.OPERATOR, cur_op));
96116
}
97117
else
98118
{
@@ -102,6 +122,13 @@ public List<Token> Parse(string equation)
102122
}
103123
}
104124

125+
// If negative was the last operator
126+
while (negCount > 0)
127+
{
128+
tokens.Add(new Token(TokenType.RBRACK, ')'));
129+
negCount--;
130+
}
131+
105132
return tokens;
106133
}
107134
}

0 commit comments

Comments
 (0)