public Object Execute(TriggerObject trigObject, bool tryReturnObject = false) { if (trigObject == null) { throw new UberScriptException("Execute: trigObject reference is null"); } var args = new List <Object> { trigObject }; // the args of the function are actually // stored in nodes--either function or argument... e.g. // EFFECT(14000,25, THIS().x, THIS().y, THIS().z) // has 2 argument nodes and 3 function nodes // each child of a FunctionNode is an argument represented by a MathTree foreach (UberNode child in Children) { if (!(child is MathTree)) { throw new UberScriptException( String.Format("Execute: MathTree child expected at line {0}:\n{1}", LineNumber, OriginalString)); } MathTree mathTree = child as MathTree; if (!mathTree.IsEmpty()) { args.Add(mathTree.Calculate(trigObject)); } } Object obj; try { // FunctionNode scriptstring contains function name obj = UberScriptFunctions.Invoke(ScriptString, args.ToArray()); } catch (Exception x) { throw new UberScriptException( String.Format("Execute: an exception was thrown during invocation at line {0}:\n{1}", LineNumber, OriginalString), x); } if (Property == null || tryReturnObject) { return(obj); } Type ptype; try { obj = PropertyGetters.GetObject(trigObject, obj, Property, out ptype); if (obj == null) { return(null); // it's ok to be null here } } catch (Exception x) { throw new UberScriptException( String.Format( "Execute: value could not be set on function '{0}' output object at line {1}:\n{2}", ScriptString, LineNumber, OriginalString), x); } if (ptype == null) { throw new UberScriptException( String.Format( "Execute: property '{0}' does not exist on function '{1}' output object at line {2}:\n{3}", Property, ScriptString, LineNumber, OriginalString)); } if (NegateOutput) { if (obj is sbyte) { obj = -(sbyte)obj; } else if (obj is short) { obj = -(short)obj; } else if (obj is int) { obj = -(int)obj; } else if (obj is long) { obj = -(long)obj; } else { throw new UberScriptException( String.Format( "Execute: output negation failed on function '{0}' output object type '{1}' at line {2}:\n{3}", ScriptString, obj.GetType(), LineNumber, OriginalString)); } } return(obj); }
public static RootNode ParseFileContents(string[] lines, string fileName) { CurrentFileBeingParsed = fileName; // ERROR HANDLING OUTSIDE OF THIS FUNCTION var balanceStack = new Stack <UberNode>(); RootNode root = new RootNode(null, null); RootNode currentRoot = root; // spawn nodes are root nodes UberNode current = root; UberNode prev = null; int currentLineNumber = 0; int openCurlyBraces = 0; var cleanedStrings = new List <CleanLine>(); foreach (string line in lines) { //Console.WriteLine(line); currentLineNumber++; StringBuilder sb = new StringBuilder(); string cleanedLine = line.Trim(); if (cleanedLine.StartsWith("//")) { continue; } bool insideQuotation = false; bool justEscapedBackslash = false; bool possibleComment = false; int forLoopRemainingSemiColons = 0; for (int i = 0; i < cleanedLine.Length; i++) { if (cleanedLine[i] == '\t') { continue; // always remove tabs... not allowed even in strings.. } if (cleanedLine[i] == '\\') { if (!insideQuotation) { throw new UberScriptException( String.Format( "Parse: unexpected escape token '\\' in '{0}' at line {1}:\n{2}", fileName, currentLineNumber, cleanedLine)); } if (!justEscapedBackslash) { if (i > 0 && cleanedLine[i - 1] == '\\') { justEscapedBackslash = true; } } else { justEscapedBackslash = false; } sb.Append('\\'); // keep the double backslash in there regardless (it is handled in MathTree.ParseOperand) continue; } if (cleanedLine[i] == '"') { if (!(i > 0 && cleanedLine[i - 1] == '\\' && !justEscapedBackslash)) { insideQuotation = !insideQuotation; } sb.Append("\""); // add \" to the string continue; } justEscapedBackslash = false; // make sure to reset this if you have gotten to this point if (insideQuotation) { sb.Append(cleanedLine[i]); continue; } if (cleanedLine[i] == '/') { if (possibleComment) { sb.Remove(sb.Length - 1, 1); // remove the previously added / character break; } possibleComment = true; sb.Append('/'); continue; } possibleComment = false; if (cleanedLine[i] == ' ' || cleanedLine[i] == '\t') { continue; } // handle lines with multiple delimiters (e.g. orc { name = "Bob"; } ) if (cleanedLine[i] == '{' || cleanedLine[i] == '}') { // check if unbalanced if (cleanedLine[i] == '{') { openCurlyBraces++; } else { openCurlyBraces--; } if (openCurlyBraces < 0) { throw new UberScriptException( String.Format( "Parse: brace mismatch detected in '{0}' at line {1}:\n{2}", fileName, currentLineNumber, cleanedLine)); } // add everything before the { or } (if there is anything) if (i > 0) { cleanedStrings.Add(new CleanLine(currentLineNumber, sb.ToString())); } cleanedStrings.Add(new CleanLine(currentLineNumber, cleanedLine[i].ToString(CultureInfo.InvariantCulture))); // add { or } on separate "line" (it's processed as a line) cleanedLine = cleanedLine.Substring(i + 1); i = -1; sb.Clear(); continue; } if (cleanedLine[i] == ';') { if (cleanedLine.Length == i + 1) { break; } // we are at the end of the loop, ignore lines ending in semicolon string stringPreviousToSemiColon = sb.ToString(); if (stringPreviousToSemiColon.Contains("for")) { if (stringPreviousToSemiColon.Contains("foreach(")) { forLoopRemainingSemiColons += 1; } else if (stringPreviousToSemiColon.Contains("for(")) // for loop has 2 semicolons { forLoopRemainingSemiColons += 2; } } if (forLoopRemainingSemiColons == 0) { // we have encountered a statement-separating semi-colon! // add everything before the { or } (if there is anything) if (i > 0) { cleanedStrings.Add(new CleanLine(currentLineNumber, sb.ToString())); } cleanedLine = cleanedLine.Substring(i + 1); i = -1; sb.Clear(); continue; } forLoopRemainingSemiColons--; } sb.Append(cleanedLine[i]); } cleanedLine = sb.ToString(); if (!String.IsNullOrWhiteSpace(cleanedLine) && !cleanedLine.StartsWith("//")) { cleanedStrings.Add(new CleanLine(currentLineNumber, sb.ToString())); } } foreach (CleanLine cleanedString in cleanedStrings) { // i have to use this CleanLine object to keep track of line numbers // in spite of {, }, and ; characters splitting the lines up! currentLineNumber = cleanedString.LineNumber; string cleanedLine = cleanedString.Value; if (cleanedLine == "" || cleanedLine.StartsWith("//")) { continue; } if (cleanedLine == "{") { balanceStack.Push(current); if (prev != null) { current = prev; } continue; } if (cleanedLine == "}") { if (balanceStack.Count > 0) { current = balanceStack.Pop(); if (current is RootNode) { // likely we have finished procesinto a spawn nodes (ChildRoots) // and therefore need to go back to the spawn node's root // so triggers are correctly assigned to it currentRoot = current.GetRootNode; } continue; } throw new UberScriptException( String.Format( "Parse: brace mismatch detected in '{0}' at line {1}:\n{2}", fileName, currentLineNumber, cleanedLine)); } // remove ending ";" characters (doesn't matter if you end with ; or not) // CANNOT do more than one statement on a line, though. // determine which type of node it is string lowerCase = cleanedLine.ToLower(); if (UberScriptTriggers.HasTrigger(lowerCase)) { prev = new TriggerNode(current, lowerCase); //Console.WriteLine(current + "->" + prev); currentRoot.TriggerNodes.Add(prev as TriggerNode); } else if (char.IsDigit(cleanedLine[0])) // must be a sequence node { int stage; if (!Int32.TryParse(cleanedLine, out stage)) { throw new UberScriptException( String.Format( "Parse: incorrect sequence format, integer expected at line {0}:\n{1}", currentLineNumber, cleanedLine)); } prev = new SequenceNode(current, cleanedLine, stage); } else if (cleanedLine.StartsWith("foreach")) { /* * objs.moblist = GETNEARBYMOBS(TRIGMOB(),5) * * foreach(objs.mob ; objs.moblist) * { * } */ prev = new ForEachNode(current, cleanedLine, currentLineNumber); } else if (cleanedLine.StartsWith("for")) { /* * for(ints.i = 0; ints.i < TRIGMOB().hits; ints.i = ints.i + 1) * { * } */ prev = new ForLoopNode(current, cleanedLine, currentLineNumber); } else if (cleanedLine.StartsWith("if") || cleanedLine.StartsWith("elif") || cleanedLine.StartsWith("else") || cleanedLine.StartsWith("elseif")) { prev = new ConditionalNode(current, cleanedLine, currentLineNumber); //Console.WriteLine(current + "->" + prev); } else if (cleanedLine.StartsWith("return")) { prev = new ReturnNode(current, cleanedLine); if (cleanedLine == "returnoverride") { ((ReturnNode)prev).Override = true; } } else { switch (cleanedLine) { case "break": { // check whether it has a foreach / for ancestor UberNode testParent = current; bool foundForNodeAncestor = false; while (testParent != null) { if (testParent is ForEachNode || testParent is ForLoopNode) { foundForNodeAncestor = true; break; } testParent = testParent.Parent; } if (!foundForNodeAncestor) { throw new UberScriptException( String.Format("Parse: unexpected 'break' statement at line {0}:\n{1}", currentLineNumber, cleanedLine)); } prev = new BreakNode(current, cleanedLine); } break; case "continue": { // check whether it has a foreach / for ancestor UberNode testParent = current; bool foundForNodeAncestor = false; while (testParent != null) { if (testParent is ForEachNode || testParent is ForLoopNode) { foundForNodeAncestor = true; break; } testParent = testParent.Parent; } if (!foundForNodeAncestor) { throw new UberScriptException( String.Format("Parse: unexpected 'continue' statement at line {0}:\n{1}", currentLineNumber, cleanedLine)); } prev = new ContinueNode(current, cleanedLine); } break; default: { if (cleanedLine.StartsWith("pause")) { double pauseDuration; if (!Double.TryParse(cleanedLine.Substring(5), out pauseDuration)) { pauseDuration = 0; } prev = new PauseNode(current, cleanedLine, pauseDuration); } else if (cleanedLine.StartsWith("function")) { prev = new UserDefinedFunctionNode(current, lowerCase); //Console.WriteLine(current + "->" + prev); root.UserDefinedFunctionNodes.Add(prev as UserDefinedFunctionNode); } /* * else if (cleanedLine.StartsWith("spawngroup")) * { * prev = new SpawnGroupNode * } * else if (cleanedLine.StartsWith("spawnregion")) * { * * } * else if (cleanedLine.StartsWith("spawnentry")) * { * * } */ else { int equalCount = 0; //char beforeChar = '\0'; int numOpenParentheses = 0; bool inQuotes = false; for (int i = 0; i < cleanedLine.Length; i++) { if (!inQuotes) { if (cleanedLine[i] == '=' && numOpenParentheses == 0) { equalCount++; } else { switch (cleanedLine[i]) { case '(': numOpenParentheses++; break; case ')': { numOpenParentheses--; if (numOpenParentheses < 0) { throw new UberScriptException( String.Format( "Parse: parentheses mismatch detected in '{0}' at line {1}:\n{2}", fileName, currentLineNumber, cleanedLine)); } } break; } } } if (cleanedLine[i] != '"') { continue; } inQuotes = !inQuotes; // assume that it's not an escaped quotation // determine if it is an escaped quotation (and that there aren't escaped backslashes preceding it) for (int ix = i - 1; ix >= 0 && cleanedLine[ix] == '\\'; ix--) { inQuotes = !inQuotes; // for each backslash behind, unescape or escape accordingly } } if (equalCount == 1) // it's a statement node { prev = new StatementNode(current, cleanedLine, currentLineNumber); //Console.WriteLine(current + "->" + prev); if (((StatementNode)prev).ContainsSpawnNode) { // global_objs.test = orc // { // name = goober // } prev.LineNumber = currentLineNumber; // we "continue" here so we set this later current.Children.Add(prev); // we "continue" here so we don't add it later //Console.WriteLine(current + "->" + prev + "... adding spawn node to statement node!"); prev = prev.Children[1]; // should contain a spawn node currentRoot.ChildRoots.Add(prev as RootNode); currentRoot = prev as RootNode; // spawn node become the new root node continue; } } else if (UberScriptFunctions.IsFunctionString(cleanedLine)) { prev = new FunctionNode(current, cleanedLine, currentLineNumber); //Console.WriteLine(current + "->" + prev); } else if (UserDefinedFunctionNode.GetUserDefinedFunctionString(cleanedLine, root) != null) { prev = new UserDefinedFunctionExectueNode(current, cleanedLine, currentLineNumber); ((UserDefinedFunctionExectueNode)prev).UserDefinedFunction = UserDefinedFunctionNode.GetUserDefinedFunctionString(cleanedLine, root); } else { // the string might have commas (indicating a spawn type with a constructor accepting arguments // such as "static,100" int commaIndex = cleanedLine.IndexOf(','); Type spawnType = ScriptCompiler.FindTypeByName(commaIndex > 0 ? cleanedLine.Substring(0, commaIndex) : cleanedLine); if (spawnType != null && typeof(Mobile).IsAssignableFrom(spawnType) || typeof(Item).IsAssignableFrom(spawnType)) { prev = new RootNode(current, cleanedLine); // keep a list of child root nodes so that spawned creatures that have triggers // specific to them can correctly point to the right place in the script e.g. // troll // { // onDeath // { // orc // { // onDeath // { // ... // } // } // } // } // --> trigger nodes are only added to their closest root node currentRoot.ChildRoots.Add(prev as RootNode); //Console.WriteLine(current + "->" + prev); currentRoot = prev as RootNode; // spawn node become the new root node } else { throw new UberScriptException( String.Format("Parse: could not parse node at line {0}:\n{1}", currentLineNumber, cleanedLine)); } } } } break; } } current.Children.Add(prev); prev.LineNumber = currentLineNumber; // used for debugging purposes } if (balanceStack.Count > 0) { throw new UberScriptException( String.Format("Parse: brace mismatch detected in '{0}' at line {1}", fileName, currentLineNumber)); } return(root); }
// returns the added operand node public static MathNode ParseOperand(string input) { if (input == null) { throw new UberScriptException("AddOperand input was null!"); } if (input == "") { throw new UberScriptException("AddOperand input was an empty string!"); } Object operand = input; OpType opType = OpType.Unassigned; try { int numCommas = 0; for (int i = 0; i < input.Length; i++) { if (input[i] == ',') { numCommas++; } } if (input.StartsWith("0x")) { try { if (isNegative) // might fail here { operand = Convert.ToInt32(input.Substring(2), 16); opType = OpType.Int; } else { operand = Convert.ToUInt64(input.Substring(2), 16); // try to use int if at all possible if ((UInt64)operand < 2147483647) // 2^31 - 1 { operand = Convert.ToInt32(input.Substring(2), 16); opType = OpType.Int; } else { opType = OpType.UInt64; } } } catch // (InvalidCastException e) { // must be a string operand = input; opType = OpType.String; } } else if (char.IsNumber(input[0]) || input[0] == '.') { double doubleVal; bool result = double.TryParse(input, out doubleVal); if (result == false) { operand = input; // must be something like 016hello, so just treat it as a string opType = OpType.String; } else { if (isNegative) { doubleVal = -doubleVal; isNegative = false; } if (!input.Contains('.') && doubleVal % 1 < 0.0009765625) // 2^-10 { // close enough to an integer value operand = (int)doubleVal; opType = OpType.Int; } else { operand = doubleVal; opType = OpType.Double; } } } else if (input == "null") { opType = OpType.Null; operand = null; } else if (input.StartsWith("\"")) { // inside a quotation--> parse out the innards and set it to a string if (!input.EndsWith("\"") || input.Length == 1) { throw new UberScriptException("ParseOperand input started with \" but did not end with \"... it must be balanced!"); } input = input.Substring(1, input.Length - 2); // remove the front and back "" bool justEscapedBackslash = false; for (int ix = 0; ix < input.Length; ix++) { if (ix > 0 && input[ix - 1] == '\\') { // not super efficient, but oh well, it's only run once if (!justEscapedBackslash) { if (input[ix] == '"') { input = input.Substring(0, ix - 1) + input.Substring(ix); // remove the \, keep the " ix--; } else if (input[ix] == 't') { input = input.Substring(0, ix - 1) + "\t" + input.Substring(ix + 1); // remove the \ and add a tab ix--; } else if (input[ix] == 'n') { input = input.Substring(0, ix - 1) + "\n" + input.Substring(ix + 1); // remove the \ and add a new line ix--; } else if (input[ix] == 'r') { input = input.Substring(0, ix - 1) + "\r" + input.Substring(ix + 1); // remove the \ and add a carriage return ix--; } else if (input[ix] == '\\') { input = input.Substring(0, ix - 1) + input.Substring(ix); // keep only one of the \ characters justEscapedBackslash = true; continue; } } // otherwise ignore it } justEscapedBackslash = false; } operand = input; opType = OpType.String; } else if (UberScriptFunctions.IsFunctionString(input)) { MathNode latestRootNode = currentRoot; // currentRoot is replaced when FunctionNode adds a mathtree for each arg currentRoot = null; operand = new FunctionNode(null, input); // can't add LineNumber here :( if (isNegative) { ((FunctionNode)operand).NegateOutput = true; isNegative = false; } opType = OpType.Func; currentRoot = latestRootNode; } // NOTE: While the performance is not as good (perhaps), I'm going to rely on parsing list accession (e.g. objs.mobs[objs.mobs.count - 1]) // on the fly (as is done in the PropertySetters and PropertyGetters functions) rather than pre-parsing it. /* * else if (UberScriptFunctions.IsArrayAccessor(input)) * { * MathNode latestRootNode = currentRoot; // currentRoot is replaced when FunctionNode adds a mathtree for each arg * currentRoot = null; * operand = new ListAccessNode(null, input); // can't add LineNumber here :( * if (isNegative) { ((ListAccessNode)operand).NegateOutput = true; isNegative = false; } * opType = OpType.ListAccess; * currentRoot = latestRootNode; * }*/ /* // I'm not going to handle literal points e.g. (1,2,3)... use the LOC function instead (so you can do math in it) * else if (numCommas > 1) // it must be a location if it is not a function and has 2 commas in it * { * operand = "(" + input + ")"; * opType = OpType.String; * }*/ else { operand = input; opType = OpType.String; } MathNode newOperandNode = new MathNode(null, null, opType, TreePriority.Operand, operand); // assigned into tree in constructor! return(newOperandNode); } catch (Exception e) { Console.WriteLine("AddOperand error: " + e.Message + "\n" + e.StackTrace); throw new UberScriptException("AddOperand error for " + input, e); } }
public Object Execute(TriggerObject trigObject, bool tryReturnObject = false) { List <Object> args = new List <Object>(); args.Add(trigObject); // every function takes this as a parameter // the args of the function are actually // stored in nodes--either function or argument... e.g. // EFFECT(14000,25, THIS().x, THIS().y, THIS().z) // has 2 argument nodes and 3 function nodes // each child of a ListAccessNode is an argument represented by a MathTree foreach (UberNode child in Children) { if (child is MathTree) { MathTree mathTree = child as MathTree; if (!mathTree.IsEmpty()) { args.Add(mathTree.Calculate(trigObject)); } //Execute(trigObject, tryReturnObject = false)); } else { throw new UberScriptException("Line " + LineNumber + ": " + OriginalString + "\nListAccessNode had children other than MathTree... something is broken!"); } } object[] reflectionArgs = new object[args.Count]; int count = 0; foreach (Object arg in args) { reflectionArgs[count] = arg; count++; } Object outputObject; /* * if (ScriptString == "THIS") { outputObject = trigObject.This; } * else if (ScriptString == "TRIGMOB") { outputObject = trigObject.TrigMob; } * else if (ScriptString == "GIVENTOTHIS") { outputObject = trigObject.DroppedOnThis; } * else if (ScriptString == "GIVENBYTHIS") { outputObject = trigObject.DroppedOnThis; } * else if (ScriptString == "TARGETTEDBY") { outputObject = trigObject.TargettedBy; } * else if (ScriptString == "TARGETTED") { outputObject = trigObject.Targetted; } * else if (ScriptString == "DAMAGE") { outputObject = trigObject.Damage; } * else*/ try { outputObject = UberScriptFunctions.Invoke(this.ScriptString, reflectionArgs); // ListAccessNode scriptstring contains function name } catch (Exception e) { throw new UberScriptException("Line " + LineNumber + ": " + OriginalString + "\nError invoking function:", e); } if (Property == null || tryReturnObject) { return(outputObject); } Type ptype; try { outputObject = PropertyGetters.GetObject(trigObject, outputObject, Property, out ptype); if (outputObject == null) { return(null); // it's ok to be null here } } catch (Exception e) { throw new UberScriptException("Line " + LineNumber + ": " + OriginalString + "\nError setting value:", e); } if (ptype == null) { throw new UberScriptException("Line " + LineNumber + ": " + OriginalString + "\nListAccessNode function " + ScriptString + " output object did not have property: " + Property); } if (NegateOutput) { Type outputType = outputObject.GetType(); if (outputType == typeof(SByte)) { outputObject = -((SByte)outputObject); } else if (outputType == typeof(Int16)) { outputObject = -((Int16)outputObject); } else if (outputType == typeof(Int32)) { outputObject = -((Int32)outputObject); } else if (outputType == typeof(Int64)) { outputObject = -((Int64)outputObject); } else { throw new UberScriptException("Line " + LineNumber + ": " + OriginalString + "\nCould not negate output of " + this.ScriptString + " output object of type: " + outputType); } } return(outputObject); }
public void ParseMath(string input) { int currentIndex = 0; Stack <MathNode> prevRootNodes = new Stack <MathNode>(); currentRoot = null; isNegative = false; int numOpenParensInFunctionCallString = 0; // should be 0 if we are not in a function call, never less than 0 int totalOpenParens = 0; // should never be less than 0 string operatorsSupportingUnaryMinus = "+-*/^%(<=>"; bool insideQuotation = false; int totalOpenSquareBracket = 0; if (input == "") { currentRoot = new MathNode(null, null, OpType.String, TreePriority.Operand, String.Empty); Children.Add(currentRoot); return; } // if it is an array, there can be nothing more in the math tree if (input.StartsWith("[") && input.EndsWith("]")) { MathNode arrayNode = new MathNode(null, input, OpType.Array, TreePriority.Infinite); string inArray = input.Substring(1, input.Length - 2); List <string> elements = new List <string>(); for (int i = 0; i < inArray.Length; i++) { char c = inArray[i]; if (insideQuotation && c != '"') { continue; } if (c == '(') { totalOpenParens += 1; } else if (c == ')') { totalOpenParens -= 1; if (totalOpenParens < 0) { throw new Exception("Unbalanced () characters in math string!"); } } else if (c == '[') { totalOpenSquareBracket += 1; } else if (c == ']') { totalOpenSquareBracket -= 1; if (totalOpenSquareBracket < 0) { throw new Exception("Unbalanced [] characters in math string!"); } } else { if (totalOpenParens > 0 || totalOpenSquareBracket > 0) { continue; } if (c == '"') { /* I'm not sure why I thought I need to do this here; it's taken care of in the MathTree parsing * if (i > 0 && inArray[i-1] == '\\') * { * // not super efficient, but oh well, it's only run once * inArray = inArray.Substring(0, i - 1) + inArray.Substring(i); // remove the \, keep the " * i--; * } * else * { * inArray = inArray.Substring(0, i) + inArray.Substring(i+1); // remove the " * i--; */ insideQuotation = !insideQuotation; //} } else if (c == ',') { elements.Add(inArray.Substring(0, i)); inArray = inArray.Substring(i + 1); i = -1; } } } if (inArray.Length > 0) { elements.Add(inArray); } // add final element ArrayList arrayList = new ArrayList(elements.Count + 10); foreach (string element in elements) { arrayList.Add(new MathTree(null, element)); } arrayNode.Value = arrayList; Children.Add(arrayNode); return; } bool justEscapedBackslash = false; for (int i = 0; i < input.Length; i++) { char c = input[i]; string arg; if (insideQuotation && c != '"') { if (c == '\\') { if (i > 0 && input[i - 1] == '\\' && !justEscapedBackslash) { justEscapedBackslash = true; continue; } } justEscapedBackslash = false; continue; } if (c == '"') { if (i > 0 && input[i - 1] == '\\' && !justEscapedBackslash) { continue; // it's an escaped \" character } else { // --> I'm putting this in the ParseOperand area //input = input.Substring(0, i) + input.Substring(i+1); // remove the " //i--; insideQuotation = !insideQuotation; } } if (totalOpenSquareBracket > 0 && c != ']') { if (c == '[') { totalOpenSquareBracket++; } continue; } else if (c == '[') { totalOpenSquareBracket++; } else if (c == ']') { if (totalOpenSquareBracket > 0) { totalOpenSquareBracket--; } else { throw new Exception("Unbalanced [] characters in list accessing string!"); } } if (numOpenParensInFunctionCallString > 0 && c != '(' && c != ')') { // don't do anything in the middle of a function string continue; } if (c == '|') { if (input[i + 1] == '|') { AddOperationNode(ref input, OpType.Or, TreePriority.Or, i, ref currentIndex); i++; } else { throw new UberScriptException("Single | encountered! Use || for logical 'or' operator. Note that the bitwise '|' operator currently not supported!"); //AddOperationNode(ref input, OpType.BitOr, TreePriority.BitAnd, i, ref currentIndex); } } else if (c == '&') { if (input[i + 1] == '&') { AddOperationNode(ref input, OpType.And, TreePriority.And, i, ref currentIndex); i++; } else { throw new UberScriptException("Single & encountered! Use && for logical 'and' operator. Note that the bitwise '&' operator currently not supported!"); //AddOperationNode(ref input, OpType.BitAnd, TreePriority.BitAnd, i, ref currentIndex); } } else if (c == '>') { if (input[i + 1] == '=') { AddOperationNode(ref input, OpType.GThanOrEqualTo, TreePriority.Comparison, i, ref currentIndex); i++; } else { AddOperationNode(ref input, OpType.GThan, TreePriority.Comparison, i, ref currentIndex); } } else if (c == '<') { if (input[i + 1] == '=') { AddOperationNode(ref input, OpType.LThanOrEqualTo, TreePriority.Comparison, i, ref currentIndex); i++; } else { AddOperationNode(ref input, OpType.LThan, TreePriority.Comparison, i, ref currentIndex); } } else if (c == '!') { if (input[i + 1] == '=') { AddOperationNode(ref input, OpType.NotEqualTo, TreePriority.Comparison, i, ref currentIndex); i++; } else { throw new UberScriptException("Cannot have a single = sign!"); } } else if (c == '=') { if (input[i + 1] == '=') { AddOperationNode(ref input, OpType.EqualTo, TreePriority.Comparison, i, ref currentIndex); i++; } else { throw new UberScriptException("Cannot have a single = sign!"); } } else if (c == '+') { AddOperationNode(ref input, OpType.Add, TreePriority.AddSub, i, ref currentIndex); } else if (c == '-') { // i == 0 means a statment is STARTING with -, e.g. -6 // otherwise there is something like this ...+-6 or ...*-6, etc if (i == 0 || (i > 0 && (operatorsSupportingUnaryMinus.Contains(input[i - 1])))) { isNegative = !isNegative; currentIndex = i + 1; } else { AddOperationNode(ref input, OpType.Sub, TreePriority.AddSub, i, ref currentIndex); } } else if (c == '*') { AddOperationNode(ref input, OpType.Mul, TreePriority.MultDiv, i, ref currentIndex); } else if (c == '/') { AddOperationNode(ref input, OpType.Div, TreePriority.MultDiv, i, ref currentIndex); } else if (c == '^') { AddOperationNode(ref input, OpType.Pow, TreePriority.Power, i, ref currentIndex); } else if (c == '%') { AddOperationNode(ref input, OpType.Mod, TreePriority.MultDiv, i, ref currentIndex); } else if (c == '(') { totalOpenParens++; // must check if it is a function call if (numOpenParensInFunctionCallString == 0) { if (UberScriptFunctions.IsFunctionString(input.Substring(currentIndex, i - currentIndex).Trim())) { numOpenParensInFunctionCallString++; continue; } // it's not a function call, it's an order of operations open parenthesis prevRootNodes.Push(currentRoot); MathNode openParenPlaceholder = new MathNode(null, null, OpType.OpenParenthesis, TreePriority.Operand); InsertIntoTree(currentRoot, openParenPlaceholder); currentRoot = openParenPlaceholder; currentIndex = i + 1; } else { numOpenParensInFunctionCallString++; // keep incrementing it } } else if (c == ')') { totalOpenParens--; if (totalOpenParens < 0) { throw new Exception("Unbalanced () characters in math string!"); } if (numOpenParensInFunctionCallString > 0) { numOpenParensInFunctionCallString--; if (numOpenParensInFunctionCallString < 0) { throw new Exception("Unbalanced () characters in function call string!"); } } else { // should be an operand or ) right before this ) arg = input.Substring(currentIndex, i - currentIndex).Trim(); if (arg == "" && !(i > 0 && input[i - 1] == ')')) { throw new UberScriptException("Math parse error: no operand before operator for input=" + input); } if (currentRoot == null) { throw new UberScriptException("Math parse error: can't have ) without a ( currentRoot! input=" + input); } // should be impossible if (arg != "") { InsertIntoTree(currentRoot, ParseOperand(arg)); } currentIndex = i + 1; MathNode prevRootNode = prevRootNodes.Pop(); currentRoot.Priority = TreePriority.Infinite; if (prevRootNode != null) { currentRoot = prevRootNode; // return up a level } // else the first character in the input was (, so there was no root node initially, so keep the currentNode } } } if (insideQuotation) { throw new UberScriptException("Unbalanced \" characters! You must terminate strings with \""); } ; if (totalOpenParens > 0) { throw new UberScriptException("Did not have matching ()!"); } if (totalOpenSquareBracket > 0) { throw new UberScriptException("Did not have matching []!"); } if (currentIndex < input.Length) { if (currentRoot == null) { currentRoot = ParseOperand(input.Substring(currentIndex, input.Length - currentIndex)); } else { InsertIntoTree(currentRoot, ParseOperand(input.Substring(currentIndex, input.Length - currentIndex))); } } Children.Add(currentRoot); }