/* 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 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= 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 ops; private static SortedList constants; private static void evaluate_stack_tops_function(Stack numStack, Stack 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 opStack, System.Collections.Generic.Stack 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>= 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(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(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 opStack = new Stack(16); Stack numStack = new Stack(16); Stack funcStack = new Stack(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