525 lines
20 KiB
C#
525 lines
20 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Data;
|
|
using System.Drawing;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Windows.Forms;
|
|
|
|
namespace Engine
|
|
{
|
|
public partial class Form1 : Form
|
|
{
|
|
private Dictionary<string, double> Symbols { get; set; } = new Dictionary<string, double>();
|
|
|
|
public Form1()
|
|
{
|
|
InitializeComponent();
|
|
}
|
|
|
|
private void Button1_Click(object sender, EventArgs e)
|
|
{
|
|
var s = textBox1.Text;
|
|
var f = FlattenInput(s);
|
|
PrintFlatList(f);
|
|
f.Reverse();
|
|
var stack = new Stack<Token>(f);
|
|
ActionsListView.Items.Clear();
|
|
PrintSymbolsTable();
|
|
button1.Text = "Result: " + HandleAddAndSub(stack);
|
|
}
|
|
|
|
void PrintSymbolsTable()
|
|
{
|
|
SymbolsListView.Items.Clear();
|
|
|
|
foreach(var k in Symbols)
|
|
{
|
|
var item = new ListViewItem(k.Key);
|
|
item.SubItems.Add(k.Value.ToString());
|
|
|
|
SymbolsListView.Items.Add(item);
|
|
}
|
|
}
|
|
|
|
void PrintFlatList(List<Token> tokens)
|
|
{
|
|
ExpressionBreakdownListView.Items.Clear();
|
|
|
|
foreach(var token in tokens)
|
|
{
|
|
var item = new ListViewItem(token.Value);
|
|
item.SubItems.Add(token.Type.ToString());
|
|
|
|
ExpressionBreakdownListView.Items.Add(item);
|
|
}
|
|
}
|
|
|
|
double HandleParenthesis(Stack<Token> tokens, bool callerHasPriority = false)
|
|
{
|
|
var runningValue = 0d;
|
|
var previousToken = new Token() { Type = TokenType.Unknown };
|
|
|
|
for(; ;)
|
|
{
|
|
if (tokens.Count == 0) break;
|
|
|
|
var currentToken = tokens.Pop();
|
|
|
|
if(currentToken.Type == TokenType.Constant)
|
|
{
|
|
previousToken = currentToken;
|
|
continue;
|
|
}
|
|
else if(currentToken.Type == TokenType.ExpStart)
|
|
{
|
|
var tmp = HandleParenthesis(tokens);
|
|
runningValue = tmp;
|
|
}
|
|
else if(currentToken.Type == TokenType.Add || currentToken.Type == TokenType.Subtract)
|
|
{
|
|
var lookAHead = tokens.Pop();
|
|
|
|
if(lookAHead.Type == TokenType.ExpStart)
|
|
{
|
|
var tmp = HandleParenthesis(tokens);
|
|
|
|
if (runningValue == 0) runningValue = Operate(Convert.ToDouble(previousToken.Value), tmp, currentToken.Type);
|
|
else runningValue = Operate(runningValue, tmp, currentToken.Type);
|
|
continue;
|
|
}
|
|
|
|
tokens.Push(lookAHead);
|
|
tokens.Push(currentToken);
|
|
if (previousToken.Type != TokenType.Unknown) tokens.Push(previousToken);
|
|
else tokens.Push(new Token { Type = TokenType.Constant, Value = runningValue.ToString() });
|
|
runningValue = HandleAddAndSub(tokens);
|
|
|
|
previousToken.Type = TokenType.Unknown;
|
|
}
|
|
else if (currentToken.Type == TokenType.Power)
|
|
{
|
|
var lookAHead = tokens.Pop();
|
|
|
|
if(lookAHead.Type == TokenType.ExpStart)
|
|
{
|
|
var powerOf = HandleParenthesis(tokens, true);
|
|
|
|
runningValue = Operate(Convert.ToDouble(previousToken.Value), powerOf, currentToken.Type);
|
|
previousToken.Type = TokenType.Unknown;
|
|
|
|
continue;
|
|
}
|
|
|
|
var tmp = Operate(Convert.ToDouble(previousToken.Value), Convert.ToDouble(lookAHead.Value), currentToken.Type);
|
|
previousToken.Type = TokenType.Unknown;
|
|
|
|
runningValue = tmp;
|
|
}
|
|
else if (currentToken.Type == TokenType.Multiply || currentToken.Type == TokenType.Divide)
|
|
{
|
|
var lookAHead = tokens.Peek();
|
|
|
|
if (lookAHead.Type == TokenType.ExpStart)
|
|
{
|
|
tokens.Pop();
|
|
|
|
var tmpe = Operate(Convert.ToDouble(previousToken.Value), HandleParenthesis(tokens), currentToken.Type);
|
|
previousToken.Type = TokenType.Unknown;
|
|
|
|
runningValue = tmpe;
|
|
|
|
continue;
|
|
}
|
|
|
|
tokens.Push(currentToken);
|
|
if (previousToken.Type != TokenType.Unknown) tokens.Push(previousToken);
|
|
else tokens.Push(new Token { Type = TokenType.Constant, Value = runningValue.ToString() });
|
|
|
|
var tmp = HandleMultAndDiv(tokens);
|
|
previousToken.Type = TokenType.Unknown;
|
|
|
|
runningValue = tmp;
|
|
}
|
|
else if (currentToken.Type == TokenType.ExpEnd)
|
|
{
|
|
if(tokens.Count > 0 && tokens.Peek().Type == TokenType.Power)
|
|
{
|
|
//Pop the power of token before returning.
|
|
tokens.Pop();
|
|
runningValue = Operate(runningValue, Convert.ToDouble(tokens.Pop().Value), TokenType.Power);
|
|
}
|
|
|
|
if(!callerHasPriority && tokens.Count > 0 && (tokens.Peek().Type == TokenType.Multiply || tokens.Peek().Type == TokenType.Divide))
|
|
{
|
|
var op = tokens.Pop();
|
|
var twoAHead = tokens.Pop();
|
|
|
|
if(twoAHead.Type == TokenType.ExpStart)
|
|
{
|
|
runningValue = Operate(runningValue, HandleParenthesis(tokens, true), op.Type);
|
|
}
|
|
else
|
|
{
|
|
runningValue = Operate(runningValue, Convert.ToDouble(twoAHead.Value), op.Type);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return runningValue;
|
|
}
|
|
|
|
double HandleMultAndDiv(Stack<Token> tokens)
|
|
{
|
|
var runningValue = 0d;
|
|
var previousToken = new Token() { Type = TokenType.Unknown };
|
|
|
|
for(; ;)
|
|
{
|
|
if (tokens.Count == 0) break;
|
|
//"Current" token.
|
|
if (tokens.Peek().Type != TokenType.Multiply && tokens.Peek().Type != TokenType.Divide && tokens.Peek().Type != TokenType.Constant && tokens.Peek().Type != TokenType.Variable)
|
|
{
|
|
break;
|
|
}
|
|
|
|
var currentToken = tokens.Pop();
|
|
|
|
if (currentToken.Type == TokenType.Constant)
|
|
{
|
|
previousToken = currentToken;
|
|
continue;
|
|
}
|
|
else if (currentToken.Type == TokenType.Multiply || currentToken.Type == TokenType.Divide)
|
|
{
|
|
var lookAHead = tokens.Pop();
|
|
|
|
if (lookAHead.Type == TokenType.ExpStart)
|
|
{
|
|
var tmp = HandleParenthesis(tokens, true);
|
|
|
|
if (runningValue == 0) runningValue = Operate(Convert.ToDouble(previousToken.Value), tmp, currentToken.Type);
|
|
else runningValue = Operate(runningValue, tmp, currentToken.Type);
|
|
previousToken.Type = TokenType.Unknown;
|
|
|
|
continue;
|
|
}
|
|
|
|
if (lookAHead.Type == TokenType.Constant)
|
|
{
|
|
if (runningValue == 0) runningValue = Operate(Convert.ToDouble(previousToken.Value), Convert.ToDouble(lookAHead.Value), currentToken.Type);
|
|
else runningValue = Operate(runningValue, Convert.ToDouble(lookAHead.Value), currentToken.Type);
|
|
|
|
previousToken.Type = TokenType.Unknown;
|
|
}
|
|
else if(lookAHead.Type == TokenType.ExpStart)
|
|
{
|
|
var tmp = HandleParenthesis(tokens, true);
|
|
|
|
if (runningValue == 0) runningValue = Operate(Convert.ToDouble(previousToken.Value), tmp, currentToken.Type);
|
|
else runningValue = Operate(runningValue, tmp, currentToken.Type);
|
|
|
|
previousToken.Type = TokenType.Unknown;
|
|
}
|
|
}
|
|
}
|
|
|
|
return runningValue;
|
|
}
|
|
|
|
double HandleAddAndSub(Stack<Token> tokens)
|
|
{
|
|
var runningValue = 0d;
|
|
var previousToken = new Token() { Type = TokenType.Unknown };
|
|
|
|
for(; ;)
|
|
{
|
|
if (tokens.Count == 0) break;
|
|
|
|
var currentToken = tokens.Pop();
|
|
|
|
if(currentToken.Type == TokenType.Constant)
|
|
{
|
|
previousToken = currentToken;
|
|
continue;
|
|
}
|
|
|
|
if (currentToken.Type == TokenType.ExpEnd)
|
|
{
|
|
tokens.Push(currentToken);
|
|
break;
|
|
}
|
|
|
|
if(currentToken.Type == TokenType.ExpStart)
|
|
{
|
|
var tmp = HandleParenthesis(tokens);
|
|
|
|
runningValue = tmp;
|
|
}
|
|
else if(currentToken.Type == TokenType.Add || currentToken.Type == TokenType.Subtract)
|
|
{
|
|
var lookAHead = tokens.Pop();
|
|
|
|
if (lookAHead.Type == TokenType.ExpStart)
|
|
{
|
|
var tmp = HandleParenthesis(tokens);
|
|
|
|
if (runningValue == 0) runningValue = Operate(Convert.ToDouble(previousToken.Value), tmp, currentToken.Type);
|
|
else runningValue = Operate(runningValue, tmp, currentToken.Type);
|
|
|
|
previousToken.Type = TokenType.Unknown;
|
|
|
|
continue;
|
|
}
|
|
|
|
if (tokens.Count > 0 && (tokens.Peek().Type == TokenType.Multiply || tokens.Peek().Type == TokenType.Divide))
|
|
{
|
|
tokens.Push(lookAHead);
|
|
var tmp = HandleMultAndDiv(tokens);
|
|
|
|
if (runningValue == 0) runningValue = Operate(Convert.ToDouble(previousToken.Value), tmp, currentToken.Type);
|
|
else runningValue = Operate(runningValue, tmp, currentToken.Type);
|
|
|
|
previousToken.Type = TokenType.Unknown;
|
|
}
|
|
else
|
|
{
|
|
if (runningValue == 0) runningValue = Operate(Convert.ToDouble(previousToken.Value), Convert.ToDouble(lookAHead.Value), currentToken.Type);
|
|
else runningValue = Operate(runningValue, Convert.ToDouble(lookAHead.Value), currentToken.Type);
|
|
|
|
previousToken.Type = TokenType.Unknown;
|
|
}
|
|
}
|
|
else if (currentToken.Type == TokenType.Multiply || currentToken.Type == TokenType.Divide)
|
|
{
|
|
tokens.Push(currentToken);
|
|
if (previousToken.Type != TokenType.Unknown) tokens.Push(previousToken);
|
|
else tokens.Push(new Token { Type = TokenType.Constant, Value = runningValue.ToString() });
|
|
var tmp = HandleMultAndDiv(tokens);
|
|
|
|
previousToken.Type = TokenType.Unknown;
|
|
//TODO: this is wrong I'm sure.
|
|
runningValue = tmp;
|
|
}
|
|
}
|
|
|
|
return runningValue;
|
|
}
|
|
|
|
List<Token> FlattenInput(string expression)
|
|
{
|
|
//if (expression.Count(x => x == '(') == 1 && expression.Count(x => x == ')') == 1) throw new Exception("Unbalanced params!");
|
|
//if(expression.Count(x => x == '(') % 2 != 0 || expression.Count(x => x == ')') % 2 != 0) throw new Exception("Unbalanced params!");
|
|
Symbols.Clear();
|
|
|
|
var tokens = new List<Token>();
|
|
var tmp = string.Empty;
|
|
var parsingNumber = false;
|
|
|
|
for(var i = 0; i < expression.Length; i++)
|
|
{
|
|
var c = expression[i];
|
|
var op = GetOperator(c);
|
|
|
|
if (c == '.' || int.TryParse(c.ToString(), out int _))
|
|
{
|
|
if(tokens.Count > 1)
|
|
{
|
|
var lookBehind = tokens[tokens.Count - 1];
|
|
|
|
if(lookBehind.Type == TokenType.ExpEnd)
|
|
{
|
|
tokens.Add(new Token() { Value = "^", Type = TokenType.Power });
|
|
}
|
|
}
|
|
|
|
if((i + 1) == expression.Length)
|
|
{
|
|
tokens.Add(new Token() { Value = $"{tmp}{c}", Type = TokenType.Constant });//new Node($"{tmp}{c}", Node.Token.Constant));
|
|
}
|
|
|
|
parsingNumber = true;
|
|
tmp += c;
|
|
|
|
continue;
|
|
}
|
|
else if(c == '(')
|
|
{
|
|
if (parsingNumber)
|
|
{
|
|
tokens.Add(new Token() { Value = tmp, Type = TokenType.Constant });
|
|
tokens.Add(new Token() { Value = "*", Type = TokenType.Multiply });
|
|
}
|
|
|
|
tokens.Add(new Token() { Value = "(", Type = TokenType.ExpStart });//new Node("(", Node.Token.ExpStart));
|
|
parsingNumber = false;
|
|
tmp = string.Empty;
|
|
continue;
|
|
}
|
|
else if(c == ')')
|
|
{
|
|
if (parsingNumber)
|
|
{
|
|
tokens.Add(new Token() { Value = tmp, Type = TokenType.Constant }); //new Node(tmp, Node.Token.Constant));
|
|
}
|
|
|
|
tokens.Add(new Token() { Value = ")", Type = TokenType.ExpEnd }); //new Node(c.ToString(), Node.Token.ExpEnd));
|
|
parsingNumber = false;
|
|
tmp = string.Empty;
|
|
continue;
|
|
}
|
|
else if(op != TokenType.Unknown)
|
|
{
|
|
if(parsingNumber)
|
|
{
|
|
tokens.Add(new Token() { Value = tmp, Type = TokenType.Constant }); //new Node(tmp, Node.Token.Constant));
|
|
}
|
|
|
|
tokens.Add(new Token() { Value = c.ToString(), Type = op });//new Node(c.ToString(), op));
|
|
parsingNumber = false;
|
|
tmp = string.Empty;
|
|
continue;
|
|
}
|
|
else if(c != ' ')
|
|
{
|
|
if (!Symbols.ContainsKey(c.ToString()))
|
|
{
|
|
Symbols.Add(c.ToString(), 1);
|
|
}
|
|
else
|
|
{
|
|
Symbols[c.ToString()]++;
|
|
}
|
|
|
|
if (tokens.Count > 1)
|
|
{
|
|
var lookBehind = tokens[tokens.Count - 1];
|
|
|
|
if (lookBehind.Type == TokenType.ExpEnd)
|
|
{
|
|
tokens.Add(new Token() { Value = "^", Type = TokenType.Power });
|
|
}
|
|
}
|
|
|
|
if ((i + 1) < expression.Length)
|
|
{
|
|
var lookAHead = expression[i + 1];
|
|
|
|
if(lookAHead == '(')
|
|
{
|
|
tokens.Add(new Token() { Value = c.ToString(), Type = TokenType.Variable });
|
|
tokens.Add(new Token() { Value = "*", Type = TokenType.Multiply });
|
|
tmp = string.Empty;
|
|
parsingNumber = false;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (parsingNumber)
|
|
{
|
|
tokens.Add(new Token() { Value = tmp, Type = TokenType.Constant });
|
|
tokens.Add(new Token() { Value = "*", Type = TokenType.Multiply });
|
|
}
|
|
|
|
tokens.Add(new Token() { Value = c.ToString(), Type = TokenType.Variable });
|
|
parsingNumber = false;
|
|
tmp = string.Empty;
|
|
}
|
|
}
|
|
|
|
return tokens;
|
|
}
|
|
|
|
double Operate(double n1, double n2, TokenType operation, [CallerMemberName] string source = "")
|
|
{
|
|
switch(operation)
|
|
{
|
|
case TokenType.Add:
|
|
ActionsListView.Items.Add($"From {source} {GetOperatorVerb(operation)} {n1} and {n2} to get {n1 + n2}");
|
|
return n1 + n2;
|
|
case TokenType.Divide:
|
|
ActionsListView.Items.Add($"From {source} {GetOperatorVerb(operation)} {n1} and {n2} to get {n1 / n2}");
|
|
return n1 / n2;
|
|
case TokenType.Multiply:
|
|
ActionsListView.Items.Add($"From {source} {GetOperatorVerb(operation)} {n1} and {n2} to get {n1 * n2}");
|
|
return n1 * n2;
|
|
case TokenType.Power:
|
|
ActionsListView.Items.Add($"From {source} {GetOperatorVerb(operation)} {n1} to the power of {n2} to get {Math.Pow(n1, n2)}");
|
|
return Math.Pow(n1, n2);
|
|
case TokenType.Subtract:
|
|
ActionsListView.Items.Add($"From {source} {GetOperatorVerb(operation)} {n1} and {n2} to get {n1 - n2}");
|
|
return n1 - n2;
|
|
default:
|
|
throw new Exception("Token must be an operator!");
|
|
}
|
|
}
|
|
|
|
TokenType GetOperator(char c)
|
|
{
|
|
switch (c)
|
|
{
|
|
case '+':
|
|
return TokenType.Add;
|
|
case '-':
|
|
return TokenType.Subtract;
|
|
case '*':
|
|
return TokenType.Multiply;
|
|
case '^':
|
|
return TokenType.Power;
|
|
case '/':
|
|
return TokenType.Divide;
|
|
default:
|
|
return TokenType.Unknown;
|
|
}
|
|
}
|
|
|
|
string GetOperatorVerb(TokenType type)
|
|
{
|
|
switch (type)
|
|
{
|
|
case TokenType.Add:
|
|
return "Adding";
|
|
case TokenType.Divide:
|
|
return "Dividing";
|
|
case TokenType.Multiply:
|
|
return "Multiplying";
|
|
case TokenType.Power:
|
|
return "Raising";
|
|
case TokenType.Subtract:
|
|
return "Subtracting";
|
|
default:
|
|
throw new Exception("Token must be an operator!");
|
|
}
|
|
}
|
|
|
|
struct Token
|
|
{
|
|
public string Value;
|
|
public TokenType Type;
|
|
}
|
|
|
|
enum TokenType
|
|
{
|
|
Add,
|
|
Subtract,
|
|
Multiply,
|
|
Divide,
|
|
Power,
|
|
Variable,
|
|
Constant,
|
|
ExpStart,
|
|
ExpEnd,
|
|
SubScript,
|
|
SuperScript,
|
|
Unknown
|
|
}
|
|
}
|
|
}
|
|
|