Just for the hell of it i made a calculator from a string lol.
I was thinking this would be a interesting task. This code is far from pretty its basically just scribbled out as fast as i could. Its probably the ugliest thing i have written in a while anyways.
It wont handle any kind of fractional powers or negative exponents but it handles the basics. Im sure it has some bugs as well. but in a console it looks cool.
if you run this test below in a console window you will get the following output.
________
Current: ((1*(3+4)*1+4^4+1*4+(2/2))-1*10+3.1415/1.579+8)
________
Parsing: ((1* *1+4^4+1*4+(2/2))-1*10+3.1415/1.579+8)
Segment: 3+4
segment=7
________
Current: ((1*7*1+4^4+1*4+(2/2))-1*10+3.1415/1.579+8)
________
Parsing: ((1*7*1+4^4+1*4+ )-1*10+3.1415/1.579+8)
Segment: 2/2
segment=1
________
Current: ((1*7*1+4^4+1*4+1)-1*10+3.1415/1.579+8)
________
Parsing: ( -1*10+3.1415/1.579+8)
Segment: 1*7*1+4^4+1*4+1
segment=1*7*1+16+1*4+1
segment=7*1+16+1*4+1
segment=7+16+1*4+1
segment=7+16+4+1
segment=23+4+1
segment=27+1
segment=28
________
Current: (28-1*10+3.1415/1.579+8)
________
Parsing:
Segment: 28-1*10+3.1415/1.579+8
segment=28-10+3.1415/1.579+8
segment=28-10+1.98955+8
segment=18+1.98955+8
segment=19.98955+8
segment=27.98955
________
Current: 27.98955
________
Final: 27.98955
answer is 27.98955
The code follows
public void Test()
{
string mathstring = "(1*(3+4)*1+4^4+1*4+(2/2))-1*10+3.1415/1.579+8";
float n = MathOperation.ParseLine(mathstring);
Console.WriteLine("answer is " + n.ToString());
}
public class MathOperation
{
static char blank = ' ';
static StringBuilder originaltext = new StringBuilder();
// index to location
static List<int> leftpara = new List<int>();
static List<int> rightpara = new List<int>();
static string[] op_string = new string[]{"Power of","Multiply by","Divide by","Add by","Subtract by"};
static char[] op_char = new char[] { '^', '*', '/', '+', '-' };
public static float ParseLine(string s)
{
float result = 0f;
originaltext.Append(s);
originaltext.Append(')');
originaltext.Insert(0,'(');
Console.WriteLine("________");
Console.WriteLine("Current: "+ originaltext.ToString());
FindParenthisisLists(originaltext);
ParseInnerMostParenthesis();
//
result = CalculateLine(originaltext);
originaltext.Length = 0;
originaltext.Insert(0, result);
originaltext = TrimAll(originaltext);
Console.WriteLine("________");
Console.WriteLine("Final: " + originaltext.ToString());
return result;
}
private static void FindParenthisisLists(StringBuilder s)
{
leftpara.Clear();
rightpara.Clear();
int i = 0;
while (i < s.Length)
{
if (s[i] == '(') {leftpara.Add(i);}
if (s[i] == ')') { rightpara.Add(i); }
i++;
}
}
private static int ParseInnerMostParenthesis()
{
int result = -1;
while (leftpara.Count > 0)
{
for (int j = 0; j < rightpara.Count; j++)
{
int temprightmostvalidpos = rightpara[j];
int templeftmostvalidpos = -1;
int lefttoremove = -1;
for (int i = 0; i < leftpara.Count; i++)
{
if (leftpara[i] < rightpara[j] && leftpara[i] > templeftmostvalidpos)
{
templeftmostvalidpos = leftpara[i];
lefttoremove = i;
}
}
// checksafetoremove
if (lefttoremove >= 0)
{
originaltext[templeftmostvalidpos] = blank;
originaltext[temprightmostvalidpos] = blank;
StringBuilder seg = new StringBuilder(16);
int k = 0;
for (int pos = templeftmostvalidpos; pos < temprightmostvalidpos; pos++)
{
seg.Append(originaltext[pos]);
originaltext[pos] = blank;
k++;
}
Console.WriteLine("________");
Console.WriteLine("Parsing: " + originaltext.ToString());
Console.WriteLine("Segment: " +seg.ToString());
float value = CalculateLine(seg);
originaltext.Insert(templeftmostvalidpos, value);
originaltext = TrimAll(originaltext);
Console.WriteLine("________");
Console.WriteLine("Current: " +originaltext.ToString());
j = rightpara.Count;
}
}
FindParenthisisLists(originaltext);
}
return result;
}
private static float CalculateLine(StringBuilder line)
{
float result = 0;
// check we have a operation left
int firstindex = 0;
while (firstindex > -1)
{
firstindex = -1;
// check for left most index in line and mark if found
int optype = -1;
// check lowest priority first
for (int i = line.Length-1; i >= 0; i--)
{
if (line[i] == '+') { optype = 3; firstindex = i; }
if (line[i] == '-') { optype = 4; firstindex = i; }
}
// this has secondary priority
for (int i = line.Length-1; i >= 0; i--)
{
if (line[i] == '*') { optype = 1; firstindex = i; }
if (line[i] == '/') { optype = 2; firstindex = i; }
}
// this one has priority
for (int i = line.Length-1; i >= 0; i--)
{
if (line[i] == '^') { optype = 0; firstindex = i; }
}
// a bi-directional sort calculate and replace
// im just going to do it all at once.
if (firstindex > -1)
{
// clear out the actual operator char that was found
line[firstindex] = blank;
// prep our loop
int left = firstindex;
int right = firstindex;
bool leftstoped = false;
bool rightstoped = false;
float leftval = 0;
float rightval = 0;
StringBuilder sbleft = new StringBuilder();
StringBuilder sbright = new StringBuilder();
int i = 0;
while (i < line.Length)
{
if (leftstoped == false)
{
left -= 1;
}
if (rightstoped == false)
{
right += 1;
}
//_____
if (left < 0) { leftstoped = true; left = 0; }
if (leftstoped == false )
{
char c = line[left];
if (c == '^' || c == '/' || c == '*' || c == '+' || c == '-')
{
leftstoped = true;
left += 1;;
}
else
{
sbleft.Insert(0,c);
line[left] = blank;
}
}
if (right >= line.Length) { rightstoped = true; right = line.Length - 1; }
if (rightstoped == false )
{
char c = line[right];
if (c == '^' || c == '/' || c == '*' || c == '+' || c == '-')
{
rightstoped = true;
right -= 1;
}
else
{
sbright.Insert(0,c);
line[right] = blank;
}
}
i++;
}
// we have exited our while loop, so we reverse the right string
sbright = Reverse(sbright);
// we parse solve and readd
if(parseFloat(sbleft.ToString(), ref leftval) && parseFloat(sbright.ToString(), ref rightval) )
{
float repval = 0f;
if (optype == 0) { for (int z = 1; z < rightval; z++) { repval = leftval * leftval; } }
if (optype == 1) { repval = leftval * rightval; }
if (optype == 2) { repval = leftval / rightval; }
if (optype == 3) { repval = leftval + rightval; }
if (optype == 4) { repval = leftval - rightval; }
string repstr = repval.ToString();
line.Insert(left, repstr);
}
else
{
Console.WriteLine(" !!!parse failed on: " + sbleft.ToString() + " or " + sbright.ToString() );
}
line = TrimAll(line);
Console.WriteLine(" segment=" + line.ToString());
}
}
parseFloat(line.ToString(), ref result);
return result;
}
/// <summary>
/// returns true on successful parse
/// if the parse fails the value remains unchanged
/// </summary>
public static bool parseFloat(string s, ref float f)
{
s = s.Trim();
bool result = false;
double tempresult = 0d;
if (double.TryParse(s, out tempresult))
{
f = (float)tempresult;
result = true;
}
else
{
// the string did not represent a double maybe it was letters
// so uh you should prolly do something to tell the user or yourself that
// Log.Tracing.Warning("Event: parsing from string to number failed");
}
return result;
}
public static StringBuilder TrimAll(StringBuilder s)
{
StringBuilder str = new StringBuilder();
for (int i = 0; i < s.Length;i++ )
{
if (s[i] != ' ') { str.Append(s[i]); }
}
s = str;
return s;
}
public static StringBuilder Reverse(StringBuilder s)
{
StringBuilder str = new StringBuilder();
str.Append(s);
for (int i = 0; i < str.Length;i++)
{
int j = (str.Length -1) - i;
s[j] = str[i];
}
return s;
}
}
ya i was getting tired when i was writing this i see a bunch of bugs in it lol
like the ^ function is wrong.
I didn’t check for the farthest left special case operator negative sign.
I dunno why i even converted it back to float after parsing.
I didn’t break half the loops when they were done early.
It makes a ton and a half of garbage lol.
Its pretty sloppy, but you could fix it up i suppose.