Files

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
}
}
}