mirror of https://github.com/odrling/Aegisub
739 lines
32 KiB
C#
739 lines
32 KiB
C#
/*
|
|
SSATool - A collection of utilities for Advanced Substation Alpha
|
|
Copyright (C) 2007 Dan Donovan
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; ONLY under version 2
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
|
|
using System;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
using System.Collections;
|
|
using System.Windows.Forms;
|
|
using System.Collections.Generic;
|
|
|
|
namespace SSATool {
|
|
public static class Evaluate {
|
|
|
|
public static string ScriptParse(string input) {
|
|
char[] charain = input.ToCharArray(); // It is over twice as fast to parse this as a character array!
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
int dIndex = 0, eIndex = 0, pIndex = 0, lastIndex = -1, parenLevel = 0, index;
|
|
bool foundds = false;
|
|
|
|
for (index=0;index<input.Length;index+=1) {
|
|
if (charain[index].Equals('$')) {
|
|
if (parenLevel == 0) {
|
|
if (index != (lastIndex+1))
|
|
sb.Append(input, lastIndex+1, index-(lastIndex+1));
|
|
dIndex = index;
|
|
foundds = true;
|
|
}
|
|
}
|
|
|
|
else if (charain[index].Equals('(')) {
|
|
if ((parenLevel == 0)) pIndex = index;
|
|
parenLevel+=1;
|
|
}
|
|
|
|
else if (charain[index].Equals(')')) {
|
|
parenLevel--;
|
|
|
|
if ((parenLevel == 0) && (foundds) && (pIndex != 0)) {
|
|
eIndex = index;
|
|
sb.Append(ScriptParse_Helper(input.Substring(dIndex+1, pIndex-(dIndex+1)), ScriptParse(input.Substring(pIndex+1, index-(pIndex+1)))));
|
|
lastIndex = eIndex;
|
|
foundds = false;
|
|
}
|
|
else if (parenLevel == 0) {
|
|
sb.Append(input.Substring(lastIndex+1, pIndex-lastIndex) + ScriptParse(input.Substring(pIndex+1, index-(pIndex+1))) + ")");
|
|
lastIndex = eIndex = index;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (input.Length > lastIndex) sb.Append(input, lastIndex+1, input.Length-(lastIndex+1));
|
|
|
|
return sb.ToString();
|
|
}
|
|
|
|
public static string ScriptParse_Helper(string function, string args) {
|
|
unsafe {
|
|
switch (function) {
|
|
case "eval":
|
|
return Evaluate.Eval(args).ToString();
|
|
case "logic":
|
|
return Evaluate.Eval(args).ToString();
|
|
case "iif":
|
|
string[] tok = args.Split(",".ToCharArray());
|
|
if (tok.Length != 3) return "error";
|
|
else if (Evaluate.Eval(tok[0]).Equals(true)) return tok[1];
|
|
else return tok[2];
|
|
case "len":
|
|
return args.Length.ToString(Util.nfi);
|
|
case "left":
|
|
string[] tokl = args.Split(",".ToCharArray());
|
|
if ((tokl.Length != 2) || (!Evaluate.isNum(tokl[1]))) return "error";
|
|
else try {
|
|
return tokl[0].Substring(0, int.Parse(tokl[1], Util.cfi));
|
|
} catch {
|
|
return "error";
|
|
}
|
|
case "mid":
|
|
string[] tokm = args.Split(",".ToCharArray());
|
|
if ((tokm.Length != 3) || (!Evaluate.isNum(tokm[1])) || (!Evaluate.isNum(tokm[2]))) return "error";
|
|
else try {
|
|
return tokm[0].Substring(int.Parse(tokm[1]), int.Parse(tokm[2], Util.cfi));
|
|
} catch {
|
|
return "error";
|
|
}
|
|
case "right":
|
|
string[] tokr = args.Split(",".ToCharArray());
|
|
if ((tokr.Length != 2) || (!Evaluate.isNum(tokr[1]))) return "error";
|
|
else try {
|
|
return tokr[0].Substring(tokr[0].Length - int.Parse(tokr[1], Util.cfi), int.Parse(tokr[1], Util.cfi));
|
|
} catch {
|
|
return "error";
|
|
}
|
|
case "str":
|
|
string[] str = args.Split(",".ToCharArray());
|
|
if ((str.Length != 2) || (!Evaluate.isNum(str[1]))) return "error";
|
|
else try {
|
|
string retstr = String.Empty;
|
|
for (int strindex=0;strindex<int.Parse(str[1], Util.cfi);strindex--)
|
|
retstr = retstr + str[0];
|
|
return retstr;
|
|
} catch {
|
|
return "error";
|
|
}
|
|
case "randlist":
|
|
string[] splitlist = args.Split(",".ToCharArray());
|
|
return splitlist[Evaluate.rand.Next(0, splitlist.Length)];
|
|
|
|
case "listindex":
|
|
string[] splitlist2 = args.Split(",".ToCharArray());
|
|
if ((splitlist2.Length < 2) || (Evaluate.isNum(splitlist2[0]) == false)) return "error";
|
|
int index = int.Parse(splitlist2[0], Util.cfi);
|
|
if ((index < 1) || (index <= splitlist2.Length)) return "error";
|
|
return splitlist2[index];
|
|
|
|
case "listindexwrap":
|
|
string[] splitlist3 = args.Split(",".ToCharArray());
|
|
if ((splitlist3.Length < 2) || (Evaluate.isNum(splitlist3[0]) == false)) return "error";
|
|
int index2 = int.Parse(splitlist3[0], Util.cfi);
|
|
if (index2 < 1) return "error";
|
|
return splitlist3[index2%splitlist3.Length];
|
|
|
|
case "listindexlast":
|
|
string[] splitlist4 = args.Split(",".ToCharArray());
|
|
if ((splitlist4.Length < 2) || (Evaluate.isNum(splitlist4[0]) == false)) return "error";
|
|
int index3 = int.Parse(splitlist4[0], Util.cfi);
|
|
if (index3 < 1) return "error";
|
|
else if (index3 >= splitlist4.Length) return splitlist4[splitlist4.Length-1];
|
|
else return splitlist4[index3];
|
|
|
|
default: // Unrecognized function... Let's treat it as a string.
|
|
return "$" + function + "(" + args + ")";
|
|
}
|
|
}
|
|
}
|
|
|
|
public static readonly Random rand = new Random();
|
|
private static SortedList<string, char> ops;
|
|
private static SortedList<string, object> constants;
|
|
|
|
private static void evaluate_stack_tops_function(Stack numStack, Stack<string> funcStack) {
|
|
short numParams = (short)numStack.Pop();
|
|
string func = funcStack.Pop();
|
|
switch (func.ToLowerInvariant()) {
|
|
case "rand" :
|
|
if (numParams > 2) throw new ArgumentException("Eval.rand");
|
|
switch (numParams) {
|
|
case 0 :
|
|
numStack.Push(rand.Next());
|
|
break;
|
|
case 1 :
|
|
int randOneR = Convert.ToInt32(numStack.Pop());
|
|
numStack.Push(rand.Next(randOneR));
|
|
break;
|
|
case 2 :
|
|
int randOne, randTwo;
|
|
randTwo = Convert.ToInt32(numStack.Pop(), Util.nfi);
|
|
randOne = Convert.ToInt32(numStack.Pop(), Util.nfi);
|
|
numStack.Push(rand.Next(randOne,randTwo));
|
|
break;
|
|
}
|
|
break;
|
|
case "randf" :
|
|
if (numParams > 2) throw new ArgumentException("Eval.randf");
|
|
|
|
switch (numParams) {
|
|
case 0 :
|
|
numStack.Push(rand.NextDouble());
|
|
break;
|
|
case 1 :
|
|
double randOneRF = (double)numStack.Pop();
|
|
numStack.Push(rand.NextDouble()*randOneRF);
|
|
break;
|
|
case 2 :
|
|
double randOneF, randTwoF;
|
|
randTwoF = (double)numStack.Pop();
|
|
randOneF = (double)numStack.Pop();
|
|
numStack.Push(randOneF + rand.NextDouble()*(randTwoF-randOneF));
|
|
break;
|
|
}
|
|
break;
|
|
case "randlist" :
|
|
int listindex = rand.Next(0, numParams - 1);
|
|
string randlist = string.Empty;
|
|
for (int i = 0; i != numParams; i += 1) {
|
|
if (i == listindex) randlist = (string)numStack.Pop();
|
|
else numStack.Pop();
|
|
}
|
|
numStack.Push(randlist);
|
|
break;
|
|
case "round" :
|
|
if (numParams != 2) throw new ArgumentException("Eval.round");
|
|
double roundme;
|
|
int dplaces;
|
|
dplaces = Convert.ToInt32(numStack.Pop(), Util.nfi);
|
|
roundme = (double) numStack.Pop();
|
|
numStack.Push(Math.Round(roundme,dplaces));
|
|
break;
|
|
case "bound" :
|
|
if (numParams != 3) throw new ArgumentException("Eval.bound");
|
|
double num, min, max;
|
|
max = (double) numStack.Pop();
|
|
min = (double) numStack.Pop();
|
|
num = (double) numStack.Pop();
|
|
num = ((num < min) ? min : num);
|
|
num = ((num > max) ? max : num);
|
|
numStack.Push(num);
|
|
break;
|
|
case "max" :
|
|
if (numParams < 2) throw new ArgumentException("Eval.max");
|
|
double ansMax;
|
|
ansMax = Math.Max((double)numStack.Pop(),(double)numStack.Pop());
|
|
for(int index=2;index!=numParams;index+=1) {
|
|
ansMax = Math.Max(ansMax,(double)numStack.Pop());
|
|
}
|
|
numStack.Push(ansMax);
|
|
break;
|
|
case "min" :
|
|
if (numParams < 2) throw new ArgumentException("Eval.min");
|
|
double ansMin;
|
|
ansMin = Math.Min((double)numStack.Pop(),(double)numStack.Pop());
|
|
for(int index=2;index!=numParams;index+=1) {
|
|
ansMin = Math.Min(ansMin,(double)numStack.Pop());
|
|
}
|
|
numStack.Push(ansMin);
|
|
break;
|
|
case "ceil" :
|
|
if (numParams != 1) throw new ArgumentException("Eval.ceil");
|
|
double ceil;
|
|
ceil = (double) numStack.Pop();
|
|
numStack.Push(Math.Ceiling(ceil));
|
|
break;
|
|
case "floor" :
|
|
if (numParams != 1) throw new ArgumentException("Eval.floor");
|
|
double floor;
|
|
floor = (double) numStack.Pop();
|
|
numStack.Push(Math.Floor(floor));
|
|
break;
|
|
case "int" :
|
|
if (numParams != 1) throw new ArgumentException("Eval.int");
|
|
double thisnum;
|
|
thisnum = (double) numStack.Pop();
|
|
numStack.Push(Convert.ToInt32(thisnum,Util.nfi));
|
|
break;
|
|
case "abs" :
|
|
if (numParams != 1) throw new ArgumentException("Eval.abs");
|
|
double abs;
|
|
abs = (double) numStack.Pop();
|
|
numStack.Push(Math.Abs(abs));
|
|
break;
|
|
case "sin" :
|
|
if (numParams != 1) throw new ArgumentException("Eval.sin");
|
|
double sin;
|
|
sin = (double) numStack.Pop();
|
|
numStack.Push(Math.Sin(sin));
|
|
break;
|
|
case "asin" :
|
|
if (numParams != 1) throw new ArgumentException("Eval.asin");
|
|
double asin;
|
|
asin = (double) numStack.Pop();
|
|
numStack.Push(Math.Asin(asin));
|
|
break;
|
|
case "cos" :
|
|
if (numParams != 1) throw new ArgumentException("Eval.cos");
|
|
double cos;
|
|
cos = (double) numStack.Pop();
|
|
numStack.Push(Math.Cos(cos));
|
|
break;
|
|
case "acos" :
|
|
if (numParams != 1) throw new ArgumentException("Eval.acos");
|
|
double acos;
|
|
acos = (double) numStack.Pop();
|
|
numStack.Push(Math.Acos(acos));
|
|
break;
|
|
case "tan" :
|
|
if (numParams != 1) throw new ArgumentException("Eval.tan");
|
|
double tan;
|
|
tan = (double) numStack.Pop();
|
|
numStack.Push(Math.Tan(tan));
|
|
break;
|
|
case "atan" :
|
|
if (numParams != 1) throw new ArgumentException("Eval.atan");
|
|
double atan;
|
|
atan = (double) numStack.Pop();
|
|
numStack.Push(Math.Atan(atan));
|
|
break;
|
|
case "atan2" :
|
|
if (numParams != 1) throw new ArgumentException("Eval.atan2");
|
|
double atanx,atany;
|
|
atanx = (double) numStack.Pop();
|
|
atany = (double) numStack.Pop();
|
|
numStack.Push(Math.Atan2(atanx,atany));
|
|
break;
|
|
case "log" :
|
|
if (numParams != 1) throw new ArgumentException("Eval.log");
|
|
double log;
|
|
log = (double) numStack.Pop();
|
|
numStack.Push(Math.Log(log));
|
|
break;
|
|
case "sqrt" :
|
|
if (numParams != 1) throw new ArgumentException("Eval.sqrt");
|
|
numStack.Push(Math.Sqrt((double)numStack.Pop()));
|
|
break;
|
|
case "len" :
|
|
if (numParams != 1) throw new ArgumentException("Eval.len");
|
|
numStack.Push(((string)numStack.Pop()).Length);
|
|
break;
|
|
case "fact" :
|
|
case "factorial" :
|
|
if (numParams != 1) throw new ArgumentException("Eval.fact");
|
|
numStack.Push(Convert.ToDouble(Factorial(Convert.ToInt32(numStack.Pop(), Util.nfi)), Util.nfi));
|
|
break;
|
|
case "gcd" :
|
|
if (numParams != 2) throw new ArgumentException("Eval.gcd");
|
|
numStack.Push(EuclidGCD(Convert.ToInt32(numStack.Pop(), Util.nfi), Convert.ToInt32(numStack.Pop(), Util.nfi)));
|
|
break;
|
|
case "lcm" :
|
|
if (numParams != 2) throw new ArgumentException("Eval.lcm");
|
|
int lcm2 = Convert.ToInt32(numStack.Pop(),Util.nfi);
|
|
int lcm1 = Convert.ToInt32(numStack.Pop(),Util.nfi);
|
|
numStack.Push((lcm1+lcm2)/EuclidGCD(lcm1,lcm2));
|
|
break;
|
|
default :
|
|
if (numParams==1 && numStack.Peek() is string) // might be a string w/o quotes, so push it back on as one
|
|
numStack.Push(func+"("+numStack.Pop()+")");
|
|
break;
|
|
}
|
|
}
|
|
private static void evaluate_stack_tops(Stack numStack, System.Collections.Generic.Stack<char> opStack, System.Collections.Generic.Stack<string> funcStack, bool safetyKill) {
|
|
if (((numStack.Count != 0) || (funcStack.Count != 0)) && (opStack.Count != 0) && (!opStack.Peek().Equals(","))) {
|
|
char Operation = opStack.Pop();
|
|
if (Operation == '@') { // @ is the placeholder for a function. The function itself is in funcStack.
|
|
evaluate_stack_tops_function(numStack, funcStack);
|
|
}
|
|
else if (Operation == '~') {
|
|
object obj = numStack.Pop();
|
|
if (obj is bool) numStack.Push(!(bool)obj);
|
|
else if (obj is int) numStack.Push(~(int)obj);
|
|
else numStack.Push(obj);
|
|
}
|
|
else if (Operation == '!') {
|
|
int fact = Convert.ToInt32(numStack.Pop(), Util.cfi);
|
|
numStack.Push(Convert.ToDouble(Factorial(fact), Util.nfi));
|
|
}
|
|
else if (numStack.Count >= 2) {
|
|
object objOne, objTwo;
|
|
objTwo = numStack.Pop();
|
|
objOne = numStack.Pop();
|
|
try {
|
|
switch (Operation) {
|
|
case 'c': // concatenation
|
|
numStack.Push((string)objOne + objTwo.ToString());
|
|
break;
|
|
case '^': // power
|
|
numStack.Push(Math.Pow((double)objOne, (double)objTwo));
|
|
break;
|
|
case '*': // multiplication
|
|
numStack.Push((double)objOne * (double)objTwo);
|
|
break;
|
|
case '/': // division
|
|
numStack.Push((double)objOne / (double)objTwo);
|
|
break;
|
|
case '%': // modulus
|
|
//numStack.Push((int)(double)objOne%(int)(double)objTwo);
|
|
numStack.Push(Math.IEEERemainder((double)objOne, (double)objTwo));
|
|
break;
|
|
case '+': // addition
|
|
numStack.Push((double)objOne + (double)objTwo);
|
|
break;
|
|
case '-': // subtraction
|
|
numStack.Push((double)objOne - (double)objTwo);
|
|
break;
|
|
case '>': // greater than
|
|
numStack.Push(doCompare(objOne, objTwo, false) >= 0);
|
|
break;
|
|
case 'G': // greater than or equal to
|
|
numStack.Push(doCompare(objOne, objTwo, false) == 1);
|
|
break;
|
|
case '<': // less than
|
|
numStack.Push(doCompare(objOne, objTwo, false) == -1);
|
|
break;
|
|
case 'L': // less than or equal to
|
|
numStack.Push(doCompare(objOne, objTwo, false) <= 0);
|
|
break;
|
|
case '=': // case-sensitive equal
|
|
numStack.Push(doCompare(objOne, objTwo, false) == 0);
|
|
break;
|
|
case 'i': // case-insensitive equal
|
|
numStack.Push(doCompare(objOne, objTwo, true) == 0);
|
|
break;
|
|
case 'N': // case-sensitive not equal
|
|
numStack.Push(doCompare(objOne, objTwo, false) != 0);
|
|
break;
|
|
case 'I': // case-insensitive not equal
|
|
numStack.Push(doCompare(objOne, objTwo, true) != 0);
|
|
break;
|
|
case '&': // bitwise-and
|
|
numStack.Push((int)objOne & (int)objTwo);
|
|
break;
|
|
case 'X': // bitwise-xor
|
|
numStack.Push((int)objOne ^ (int)objTwo);
|
|
break;
|
|
case '|': // bitwise-or
|
|
numStack.Push((int)objOne | (int)objTwo);
|
|
break;
|
|
case 'A': // boolean and
|
|
numStack.Push((bool)objOne && (bool)objTwo);
|
|
break;
|
|
case 'O': // boolean or
|
|
numStack.Push((bool)objOne || (bool)objTwo);
|
|
break;
|
|
case 'r': // shift right
|
|
numStack.Push((int)objOne >> (int)objTwo);
|
|
break;
|
|
case 'l': // shift left
|
|
numStack.Push((int)objOne << (int)objTwo);
|
|
break;
|
|
}
|
|
} catch { throw new FormatException(); }
|
|
}
|
|
// Can't make sense of the stacks, so just clear them...
|
|
else if (safetyKill) {
|
|
funcStack.Clear();
|
|
opStack.Clear();
|
|
numStack.Clear();
|
|
}
|
|
}
|
|
|
|
}
|
|
private static int doCompare(object objOne, object objTwo, bool caseIns) {
|
|
if (objOne is string || objTwo is string)
|
|
return String.Compare(objOne.ToString(), objTwo.ToString(), caseIns);
|
|
else if (objOne is bool && objTwo is bool)
|
|
return ((bool)objOne).CompareTo((bool)objTwo);
|
|
else if (objOne is double && objTwo is double)
|
|
return ((double)objOne).CompareTo((double)objTwo);
|
|
else if (objOne is bool && objTwo is double)
|
|
return ((bool)objOne).CompareTo((double)objTwo!=0.0);
|
|
else if (objOne is double && objTwo is bool)
|
|
return ((double)objOne!=0.0).CompareTo((bool)objTwo);
|
|
/*
|
|
else if (objOne is int && objTwo is int)
|
|
return ((int)objOne).CompareTo((int)objTwo);
|
|
else if (objOne is bool && objTwo is int)
|
|
return ((bool)objOne).CompareTo((int)objTwo!=0);
|
|
else if (objOne is int && objTwo is bool)
|
|
return ((int)objOne!=0).CompareTo((bool)objTwo);
|
|
else if (objOne is double && objTwo is int)
|
|
return ((double)objOne).CompareTo((double)(int)objTwo);
|
|
else if (objOne is int && objTwo is double)
|
|
return ((double)(int)objOne).CompareTo((double)objTwo);
|
|
*/
|
|
throw new ArgumentException("Eval.doCompare");
|
|
}
|
|
public static bool isNum(string input) {
|
|
if (input.Length == 0) return false;
|
|
char[] charain = input.ToCharArray();
|
|
for (int index=(input.StartsWith("-")?1:0);index<input.Length;index+=1)
|
|
if ((!char.IsDigit(charain[index])) && (charain[index] != '.')) return false;
|
|
return true;
|
|
}
|
|
public static int EuclidGCD(int u, int v) {
|
|
int k = 0;
|
|
if ((u|v)==0) return 0;
|
|
while (((u|v)&1)==0) { /* while both u and v are even */
|
|
u >>= 1; /* shift u right, dividing it by 2 */
|
|
v >>= 1; /* shift v right, dividing it by 2 */
|
|
k+=1; /* add a power of 2 to the final result */
|
|
}
|
|
/* At this point either u or v (or both) is odd */
|
|
do {
|
|
if ((u & 1) == 0) /* if u is even */
|
|
u >>= 1; /* divide u by 2 */
|
|
else if ((v & 1) == 0) /* else if v is even */
|
|
v >>= 1; /* divide v by 2 */
|
|
else if (u >= v) /* u and v are both odd */
|
|
u = (u-v) >> 1;
|
|
else /* u and v both odd, v > u */
|
|
v = (v-u) >> 1;
|
|
} while (u > 0);
|
|
return v << k; /* returns v * 2^k */
|
|
}
|
|
public static int Factorial(int x) {
|
|
int result=1;
|
|
for (int index = x; index != 0; index--) {
|
|
result *= index;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private static byte opOrder(char tOp) {
|
|
byte retVal;
|
|
switch (tOp) {
|
|
case '!': // factorial
|
|
retVal = 20;
|
|
break;
|
|
case '@': // function
|
|
retVal=16;
|
|
break;
|
|
case ',': // function argument separator
|
|
retVal=15;
|
|
break;
|
|
case 'c': // concatenation
|
|
retVal=14;
|
|
break;
|
|
case '~': // inversion
|
|
retVal=13;
|
|
break;
|
|
case '^': // power
|
|
retVal=12;
|
|
break;
|
|
case '*': // multiplication
|
|
case '/': // division
|
|
case '%': // modulus
|
|
case '\\': // integer division
|
|
retVal=11;
|
|
break;
|
|
case '+': // addition
|
|
case '-': // subtraction
|
|
retVal=10;
|
|
break;
|
|
case '>': // greater than
|
|
case 'G': // greater than or equal to
|
|
case '<': // less than
|
|
case 'L': // less than or equal to
|
|
retVal=8;
|
|
break;
|
|
case '=': // case-sensitive equal
|
|
case 'i': // case-insensitive equal
|
|
case 'N': // case-sensitive not equal
|
|
case 'I': // case-insensitive not equal
|
|
retVal=7;
|
|
break;
|
|
case '&': // bitwise-and
|
|
retVal=6;
|
|
break;
|
|
case 'X': // bitwise-xor
|
|
retVal=5;
|
|
break;
|
|
case '|': // bitwise-or
|
|
retVal=4;
|
|
break;
|
|
case 'A': // boolean and
|
|
retVal=3;
|
|
break;
|
|
case 'O': // boolean or
|
|
retVal=2;
|
|
break;
|
|
case 'r': // shift right
|
|
case 'l': // shift left
|
|
retVal=9;
|
|
break;
|
|
default: // not an operation
|
|
retVal=0;
|
|
break;
|
|
}
|
|
return retVal;
|
|
}
|
|
public static bool isOp(char i, short pos) {
|
|
if (pos==1) return ("!+-*/\\%^&|=<>~".IndexOf(i) != -1);
|
|
else if (pos==2) return ("&|^=)".IndexOf(i) != -1);
|
|
else if (pos==3) return i=='=';
|
|
return false;
|
|
}
|
|
public static void FillOpsList() {
|
|
ops = new SortedList<string, char>(23);
|
|
ops.Add(">>", 'r');
|
|
ops.Add(">=", 'G');
|
|
ops.Add(">", '>');
|
|
ops.Add("===", '=');
|
|
ops.Add("==", '=');
|
|
ops.Add("=", 'i');
|
|
ops.Add("<=", 'L');
|
|
ops.Add("<<", 'l');
|
|
ops.Add("<", '<');
|
|
ops.Add("+", '+');
|
|
ops.Add("||", 'O');
|
|
ops.Add("|", '|');
|
|
ops.Add("^^", 'X');
|
|
ops.Add("^", '^');
|
|
ops.Add("/", '/');
|
|
ops.Add("*", '*');
|
|
ops.Add("&&", 'A');
|
|
ops.Add("&", '&');
|
|
ops.Add("%", '%');
|
|
ops.Add("-", '-');
|
|
ops.Add("!==", 'I');
|
|
ops.Add("!=", 'N');
|
|
ops.Add("!", '!');
|
|
ops.Add("~", '~');
|
|
|
|
constants = new SortedList<string, object>(6);
|
|
constants.Add("e", Math.E);
|
|
constants.Add("false",false);
|
|
constants.Add("g", 1.6180339887498948482045868);
|
|
constants.Add("pi", Math.PI);
|
|
constants.Add("true", true);
|
|
constants.Add("y", 0.5772156649015328606065120);
|
|
}
|
|
|
|
public static object Eval(string input) {
|
|
char[] charain = input.ToCharArray();
|
|
Stack<char> opStack = new Stack<char>(16);
|
|
Stack numStack = new Stack(16);
|
|
Stack<string> funcStack = new Stack<string>(8);
|
|
|
|
string buff;
|
|
int i = 0, pLevel;
|
|
char thisOp;
|
|
short loop, numParams;
|
|
bool lastwasop = true;
|
|
bool isnumber, isfloat, infunc, inquotes, unknownString = false;
|
|
|
|
while (i < charain.Length) {
|
|
if (charain[i].Equals('(')) {
|
|
pLevel = 1;
|
|
buff = string.Empty;
|
|
while (pLevel!=0 && ++i<charain.Length) {
|
|
if (charain[i].Equals('(')) pLevel+=1;
|
|
if (charain[i].Equals(')')) pLevel--;
|
|
if (pLevel!=0 || !charain[i].Equals(')')) buff += charain[i];
|
|
}
|
|
numStack.Push(Eval(buff));
|
|
lastwasop = false;
|
|
}
|
|
else if ((!lastwasop||charain[i]!='-'||(i!=0&&charain[i-1]=='!')) && isOp(charain[i], 1)) {
|
|
buff = charain[i].ToString(Util.cfi);
|
|
loop=1;
|
|
while (++i<charain.Length && loop++<=3 && isOp(charain[i], loop))
|
|
buff += charain[i];
|
|
i--;
|
|
thisOp = ops[buff];
|
|
if (!unknownString && thisOp.Equals('+') && numStack.Count!=0 && numStack.Peek() is string) thisOp='c';
|
|
while (opStack.Count!=0 && numStack.Count!=0 && opOrder(thisOp)<=opOrder(opStack.Peek()))
|
|
evaluate_stack_tops(numStack, opStack, funcStack, false);
|
|
opStack.Push(thisOp);
|
|
lastwasop=true;
|
|
unknownString = false;
|
|
}
|
|
|
|
else if (charain[i] != ' ') {
|
|
buff = string.Empty;
|
|
if (charain[i].Equals('"')) {
|
|
while (i++!=charain.Length && charain[i]!='"')
|
|
buff += charain[i];
|
|
numStack.Push(buff);
|
|
}
|
|
else {
|
|
isnumber = true;
|
|
isfloat = infunc = inquotes = false;
|
|
pLevel = numParams = 0;
|
|
|
|
if (charain[i].Equals('-')) {
|
|
buff = "-";
|
|
i+=1;
|
|
}
|
|
while (i!=charain.Length && (!isOp(charain[i], 1) || infunc)) {
|
|
if (charain[i].Equals('(')) {
|
|
if (pLevel==0) {
|
|
funcStack.Push(buff);
|
|
buff = string.Empty;
|
|
opStack.Push('@');
|
|
infunc = true;
|
|
}
|
|
else buff += '(';
|
|
pLevel+=1;
|
|
}
|
|
else if (infunc && charain[i]==')') {
|
|
pLevel--;
|
|
if (pLevel==0) {
|
|
numStack.Push(Eval(buff));
|
|
numParams+=1;
|
|
}
|
|
else buff += ')';
|
|
}
|
|
else if (infunc && pLevel==1 && charain[i]==',') {
|
|
numStack.Push(Eval(buff));
|
|
numParams+=1;
|
|
buff = string.Empty;
|
|
}
|
|
else {
|
|
if (inquotes || charain[i]!=' ') {
|
|
if (charain[i] == '"') inquotes = !inquotes;
|
|
if (!(char.IsDigit(charain[i])||charain[i]=='.')) isnumber = false;
|
|
if (charain[i] == '.') isfloat=true;
|
|
buff += charain[i];
|
|
}
|
|
}
|
|
i+=1;
|
|
}
|
|
|
|
if (!infunc) {
|
|
if (isnumber) {
|
|
if (isfloat = true) numStack.Push(double.Parse(buff, Util.cfi)); // effectively disable ints (for now, at least)
|
|
else numStack.Push(int.Parse(buff, Util.cfi));
|
|
}
|
|
|
|
else if (constants.ContainsKey(buff.ToLowerInvariant())) numStack.Push(constants[buff.ToLowerInvariant()]);
|
|
|
|
else {
|
|
numStack.Push(buff);
|
|
//unknownString = true;
|
|
}
|
|
}
|
|
else numStack.Push(numParams);
|
|
i--;
|
|
}
|
|
|
|
lastwasop = false;
|
|
}
|
|
|
|
i+=1;
|
|
}
|
|
|
|
while (opStack.Count!=0 && numStack.Count!=0)
|
|
evaluate_stack_tops(numStack, opStack, funcStack, false);
|
|
|
|
if (numStack.Count != 0) return numStack.Pop();
|
|
else return false;
|
|
}
|
|
}
|
|
} |