forked from Giorgi/Math-Expression-Evaluator
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
First implementation of Shunting yard algorithm
- Loading branch information
Showing
12 changed files
with
11,221 additions
and
4 deletions.
There are no files selected for viewing
86 changes: 86 additions & 0 deletions
86
SimpleExpressionEvaluator.Tests/ExpressionEvaluatorTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Text; | ||
|
||
using NUnit.Framework; | ||
|
||
namespace SimpleExpressionEvaluator.Tests | ||
{ | ||
public class ExpressionEvaluatorTests | ||
{ | ||
private ExpressionEvaluator engine; | ||
private Random generator; | ||
|
||
[TestFixtureSetUp] | ||
public void SetUp() | ||
{ | ||
engine = new ExpressionEvaluator(); | ||
generator = new Random(); | ||
} | ||
|
||
[Test] | ||
public void Empty_String_Is_Zero() | ||
{ | ||
Assert.That(engine.Evaluate(""), Is.EqualTo(0)); | ||
} | ||
|
||
[Test] | ||
public void Decimal_Is_Treated_As_Decimal() | ||
{ | ||
var left = generator.Next(1, 100); | ||
|
||
Assert.That(engine.Evaluate(left.ToString()), Is.EqualTo(left)); | ||
} | ||
|
||
[Test] | ||
public void Two_Plus_Two_Is_Four() | ||
{ | ||
Assert.That(engine.Evaluate("2+2"), Is.EqualTo(4)); | ||
} | ||
|
||
[Test] | ||
public void Can_Add_Two_Decimal_Numbers() | ||
{ | ||
Assert.That(engine.Evaluate("2.7+3.2"), Is.EqualTo(2.7m + 3.2m)); | ||
} | ||
|
||
[Test] | ||
public void Can_Add_Many_Numbers() | ||
{ | ||
Assert.That(engine.Evaluate("1.2+3.4+5.6+7.8"), Is.EqualTo(1.2m + 3.4m + 5.6m + 7.8m)); | ||
Assert.That(engine.Evaluate("1.7+2.9+14.24+6.58"), Is.EqualTo(1.7m + 2.9m + 14.24m + 6.58m)); | ||
} | ||
|
||
[Test] | ||
public void Can_Subtract_Two_Numbers() | ||
{ | ||
Assert.That(engine.Evaluate("5-2"), Is.EqualTo(5 - 2)); | ||
} | ||
|
||
[Test] | ||
public void Can_Subtract_Multiple_Numbers() | ||
{ | ||
Assert.That(engine.Evaluate("15.2-2.3-4.8-0.58"), Is.EqualTo(15.2m - 2.3m - 4.8m - 0.58m)); | ||
} | ||
|
||
[Test] | ||
public void Can_Add_And_Subtract_Multiple_Numbers() | ||
{ | ||
Assert.That(engine.Evaluate("15+8-4-2+7"), Is.EqualTo(15 + 8 - 4 - 2 + 7)); | ||
Assert.That(engine.Evaluate("17.89-2.47+7.16"), Is.EqualTo(17.89m - 2.47m + 7.16m)); | ||
|
||
} | ||
|
||
[Test] | ||
public void Can_Add_Subtract_Multiply_Divide_Multiple_Numbers() | ||
{ | ||
Assert.That(engine.Evaluate("50-5*3*2+7"), Is.EqualTo(50 - 5 * 3 * 2 + 7)); | ||
Assert.That(engine.Evaluate("84+15+4-4*3*9+24+4-54/3-5-7+47"), Is.EqualTo(84 + 15 + 4 - 4 * 3 * 9 + 24 + 4 - 54 / 3 - 5 - 7 + 47)); | ||
Assert.That(engine.Evaluate("50-48/4/3+7*2*4+2+5+8"), Is.EqualTo(50 - 48 / 4 / 3 + 7 * 2 * 4 + 2 + 5 + 8)); | ||
Assert.That(engine.Evaluate("5/2/2+1.5*3+4.58"), Is.EqualTo(5 / 2m / 2m + 1.5m * 3m + 4.58m)); | ||
Assert.That(engine.Evaluate("25/3+1.34*2.56+1.49+2.36/1.48"), Is.EqualTo(25 / 3m + 1.34m * 2.56m + 1.49m + 2.36m / 1.48m)); | ||
Assert.That(engine.Evaluate("2*3+5-4-2*5+7"), Is.EqualTo(2 * 3 + 5 - 4 - 2 * 5 + 7)); | ||
} | ||
} | ||
} |
36 changes: 36 additions & 0 deletions
36
SimpleExpressionEvaluator.Tests/Properties/AssemblyInfo.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
using System.Reflection; | ||
using System.Runtime.CompilerServices; | ||
using System.Runtime.InteropServices; | ||
|
||
// General Information about an assembly is controlled through the following | ||
// set of attributes. Change these attribute values to modify the information | ||
// associated with an assembly. | ||
[assembly: AssemblyTitle("SimpleExpressionEvaluator.Tests")] | ||
[assembly: AssemblyDescription("")] | ||
[assembly: AssemblyConfiguration("")] | ||
[assembly: AssemblyCompany("")] | ||
[assembly: AssemblyProduct("SimpleExpressionEvaluator.Tests")] | ||
[assembly: AssemblyCopyright("Copyright © 2012")] | ||
[assembly: AssemblyTrademark("")] | ||
[assembly: AssemblyCulture("")] | ||
|
||
// Setting ComVisible to false makes the types in this assembly not visible | ||
// to COM components. If you need to access a type in this assembly from | ||
// COM, set the ComVisible attribute to true on that type. | ||
[assembly: ComVisible(false)] | ||
|
||
// The following GUID is for the ID of the typelib if this project is exposed to COM | ||
[assembly: Guid("95807a00-691e-4cb4-be31-acb06cacf350")] | ||
|
||
// Version information for an assembly consists of the following four values: | ||
// | ||
// Major Version | ||
// Minor Version | ||
// Build Number | ||
// Revision | ||
// | ||
// You can specify all the values or you can default the Build and Revision Numbers | ||
// by using the '*' as shown below: | ||
// [assembly: AssemblyVersion("1.0.*")] | ||
[assembly: AssemblyVersion("1.0.0.0")] | ||
[assembly: AssemblyFileVersion("1.0.0.0")] |
66 changes: 66 additions & 0 deletions
66
SimpleExpressionEvaluator.Tests/SimpleExpressionEvaluator.Tests.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http:https://schemas.microsoft.com/developer/msbuild/2003"> | ||
<PropertyGroup> | ||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | ||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> | ||
<ProductVersion>8.0.30703</ProductVersion> | ||
<SchemaVersion>2.0</SchemaVersion> | ||
<ProjectGuid>{08F4484E-5FD8-4590-A8D7-12FBE47120C8}</ProjectGuid> | ||
<OutputType>Library</OutputType> | ||
<AppDesignerFolder>Properties</AppDesignerFolder> | ||
<RootNamespace>SimpleExpressionEvaluator.Tests</RootNamespace> | ||
<AssemblyName>SimpleExpressionEvaluator.Tests</AssemblyName> | ||
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion> | ||
<FileAlignment>512</FileAlignment> | ||
</PropertyGroup> | ||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> | ||
<DebugSymbols>true</DebugSymbols> | ||
<DebugType>full</DebugType> | ||
<Optimize>false</Optimize> | ||
<OutputPath>bin\Debug\</OutputPath> | ||
<DefineConstants>DEBUG;TRACE</DefineConstants> | ||
<ErrorReport>prompt</ErrorReport> | ||
<WarningLevel>4</WarningLevel> | ||
</PropertyGroup> | ||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> | ||
<DebugType>pdbonly</DebugType> | ||
<Optimize>true</Optimize> | ||
<OutputPath>bin\Release\</OutputPath> | ||
<DefineConstants>TRACE</DefineConstants> | ||
<ErrorReport>prompt</ErrorReport> | ||
<WarningLevel>4</WarningLevel> | ||
</PropertyGroup> | ||
<ItemGroup> | ||
<Reference Include="nunit.framework"> | ||
<HintPath>..\packages\NUnit.2.6.0.12054\lib\nunit.framework.dll</HintPath> | ||
</Reference> | ||
<Reference Include="System" /> | ||
<Reference Include="System.Core" /> | ||
<Reference Include="System.Xml.Linq" /> | ||
<Reference Include="System.Data.DataSetExtensions" /> | ||
<Reference Include="Microsoft.CSharp" /> | ||
<Reference Include="System.Data" /> | ||
<Reference Include="System.Xml" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<Compile Include="ExpressionEvaluatorTests.cs" /> | ||
<Compile Include="Properties\AssemblyInfo.cs" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<None Include="packages.config" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<ProjectReference Include="..\SimpleExpressionEvaluator\SimpleExpressionEvaluator.csproj"> | ||
<Project>{30637214-EDE9-4C2E-BFD6-E4B163FA308B}</Project> | ||
<Name>SimpleExpressionEvaluator</Name> | ||
</ProjectReference> | ||
</ItemGroup> | ||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | ||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. | ||
Other similar extension points exist, see Microsoft.Common.targets. | ||
<Target Name="BeforeBuild"> | ||
</Target> | ||
<Target Name="AfterBuild"> | ||
</Target> | ||
--> | ||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<packages> | ||
<package id="NUnit" version="2.6.0.12054" /> | ||
</packages> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,166 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Text; | ||
using System.IO; | ||
using System.Linq.Expressions; | ||
|
||
namespace SimpleExpressionEvaluator | ||
{ | ||
public class ExpressionEvaluator | ||
{ | ||
public decimal Evaluate(string expression) | ||
{ | ||
if (string.IsNullOrWhiteSpace(expression)) | ||
{ | ||
return 0; | ||
} | ||
|
||
var expressionStack = new Stack<Expression>(); | ||
var operatorStack = new Stack<char>(); | ||
|
||
using (var reader = new StringReader(expression)) | ||
{ | ||
int peek; | ||
while ((peek = reader.Peek()) > -1) | ||
{ | ||
var next = (char)peek; | ||
|
||
if (char.IsDigit(next)) | ||
{ | ||
expressionStack.Push(ReadOperand(reader)); | ||
continue; | ||
} | ||
|
||
if (Operation.IsDefined(next)) | ||
{ | ||
var currentOperation = ReadOperation(reader); | ||
|
||
while (true) | ||
{ | ||
if (operatorStack.Count == 0) | ||
{ | ||
operatorStack.Push(next); | ||
break; | ||
} | ||
|
||
var lastOperition = operatorStack.Peek(); | ||
|
||
if (currentOperation.Precedence > ((Operation)lastOperition).Precedence) | ||
{ | ||
operatorStack.Push(next); | ||
break; | ||
} | ||
|
||
var right = expressionStack.Pop(); | ||
var left = expressionStack.Pop(); | ||
|
||
expressionStack.Push(((Operation)operatorStack.Pop()).Apply(left, right)); | ||
} | ||
continue; | ||
} | ||
|
||
if (next != ' ') | ||
{ | ||
throw new ArgumentException("Invalid character encountered", "expression"); | ||
} | ||
} | ||
} | ||
|
||
while (operatorStack.Count > 0) | ||
{ | ||
var right = expressionStack.Pop(); | ||
var left = expressionStack.Pop(); | ||
|
||
expressionStack.Push(((Operation)operatorStack.Pop()).Apply(left, right)); | ||
} | ||
|
||
var compiled = Expression.Lambda<Func<decimal>>(expressionStack.Pop()).Compile(); | ||
return compiled(); | ||
} | ||
|
||
private Expression ReadOperand(StringReader reader) | ||
{ | ||
var operand = string.Empty; | ||
|
||
int peek; | ||
|
||
while ((peek = reader.Peek()) > -1) | ||
{ | ||
var next = (char)peek; | ||
|
||
if (char.IsDigit(next) || next == '.') | ||
{ | ||
reader.Read(); | ||
operand += next; | ||
} | ||
else | ||
{ | ||
break; | ||
} | ||
} | ||
|
||
return Expression.Constant(string.IsNullOrEmpty(operand) ? decimal.Zero : decimal.Parse(operand)); | ||
} | ||
|
||
private Operation ReadOperation(TextReader reader) | ||
{ | ||
var operation = (char)reader.Read(); | ||
return (Operation)operation; | ||
} | ||
} | ||
|
||
internal sealed class Operation | ||
{ | ||
private readonly int precedence; | ||
private readonly string name; | ||
private readonly Func<Expression, Expression, Expression> operation; | ||
|
||
public static readonly Operation Addition = new Operation(1, Expression.Add, "Addition"); | ||
public static readonly Operation Subtraction = new Operation(1, Expression.Subtract, "Subtraction"); | ||
public static readonly Operation Multiplication = new Operation(2, Expression.Multiply, "Multiplication"); | ||
public static readonly Operation Division = new Operation(2, Expression.Divide, "Division"); | ||
|
||
private static readonly Dictionary<char, Operation> Operations = new Dictionary<char, Operation> | ||
{ | ||
{ '+', Addition }, | ||
{ '-', Subtraction }, | ||
{ '*', Multiplication}, | ||
{ '/', Division } | ||
}; | ||
|
||
private Operation(int precedence, Func<Expression, Expression, Expression> operation, string name) | ||
{ | ||
this.precedence = precedence; | ||
this.operation = operation; | ||
this.name = name; | ||
} | ||
|
||
public int Precedence | ||
{ | ||
get { return precedence; } | ||
} | ||
|
||
public static explicit operator Operation(char operation) | ||
{ | ||
Operation result; | ||
|
||
if (Operations.TryGetValue(operation, out result)) | ||
{ | ||
return result; | ||
} | ||
else | ||
{ | ||
throw new InvalidCastException(); | ||
} | ||
} | ||
|
||
public Expression Apply(Expression left, Expression right) | ||
{ | ||
return operation(left, right); | ||
} | ||
|
||
public static bool IsDefined(char operation) | ||
{ | ||
return Operations.ContainsKey(operation); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
Binary file not shown.
Oops, something went wrong.