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); }
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); } }
// 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 override ProcessResult Process(ProcessResult lastSiblingResult, TriggerObject trigObject) { // will always have 2 children, either function nodes or MathTree nodes Object o = null; string propname = null; Object value; string globalObjsString; string localObjsString; // 1st child is the property that will be set if (Children[0] is FunctionNode) { // this presumes that it is a function like THIS() or TRIGMOB() FunctionNode fn = Children[0] as FunctionNode; o = fn.Execute(trigObject, true); propname = fn.Property; } else // Children[0] is argument node { string scriptString = Children[0].ScriptString; int dotIndex = scriptString.IndexOf('.'); if (dotIndex > -1) { if (scriptString.StartsWith("global_")) { if (scriptString.StartsWith("global_ints.")) { UberTreeParser.global_ints[scriptString.Substring(12)] = (int)((MathTree)Children[1]).Calculate(trigObject); value = ((MathTree)Children[1]).Calculate(trigObject); switch (AssignType) { case AssignmentType.Plus: UberTreeParser.global_ints[scriptString.Substring(12)] = UberTreeParser.global_ints[scriptString.Substring(12)] + Convert.ToInt32(value); break; case AssignmentType.Minus: UberTreeParser.global_ints[scriptString.Substring(12)] = UberTreeParser.global_ints[scriptString.Substring(12)] - Convert.ToInt32(value); break; case AssignmentType.Mult: UberTreeParser.global_ints[scriptString.Substring(12)] = UberTreeParser.global_ints[scriptString.Substring(12)] * Convert.ToInt32(value); break; case AssignmentType.Divide: UberTreeParser.global_ints[scriptString.Substring(12)] = Convert.ToInt32(UberTreeParser.global_ints[scriptString.Substring(12)] / Convert.ToDouble(value)); break; default: UberTreeParser.global_ints[scriptString.Substring(12)] = Convert.ToInt32(value); break; } return(ProcessResult.None); } if (scriptString.StartsWith("global_strings.")) { value = GetReturnObject(Children[1], trigObject); if (value != null) { value = value.ToString(); switch (AssignType) { case AssignmentType.Plus: UberTreeParser.global_strings[scriptString.Substring(15)] = UberTreeParser.global_strings[scriptString.Substring(15)] + (string)value; break; case AssignmentType.Regular: UberTreeParser.global_strings[scriptString.Substring(15)] = (string)value; break; } throw new UberScriptException( "Line " + LineNumber + ": " + ScriptString + "\nTried to use +=, -=, *=, or /= but this is only supported for " + value.GetType() + "! It is only supported for int, double, and UInt64"); //return ProcessResult.None; } throw new UberScriptException( "Line " + LineNumber + ": " + ScriptString + "\nTried to add null value to strings dictionary... this is not allowed!"); } if (scriptString.StartsWith("global_doubles.")) { value = ((MathTree)Children[1]).Calculate(trigObject); switch (AssignType) { case AssignmentType.Plus: UberTreeParser.global_doubles[scriptString.Substring(15)] = UberTreeParser.global_doubles[scriptString.Substring(15)] + Convert.ToDouble(value); break; case AssignmentType.Minus: UberTreeParser.global_doubles[scriptString.Substring(15)] = UberTreeParser.global_doubles[scriptString.Substring(15)] - Convert.ToDouble(value); break; case AssignmentType.Mult: UberTreeParser.global_doubles[scriptString.Substring(15)] = UberTreeParser.global_doubles[scriptString.Substring(15)] * Convert.ToDouble(value); break; case AssignmentType.Divide: UberTreeParser.global_doubles[scriptString.Substring(15)] = UberTreeParser.global_doubles[scriptString.Substring(15)] / Convert.ToDouble(value); break; default: UberTreeParser.global_doubles[scriptString.Substring(15)] = Convert.ToDouble(value); break; } return(ProcessResult.None); } if (scriptString.StartsWith("global_objs.")) { globalObjsString = scriptString.Substring(12); int secondDotIndex = globalObjsString.IndexOf('.'); if (secondDotIndex > -1) // we are trying to set a property on an existing object { // will throw error if it doesn't exist yet o = UberTreeParser.global_objs[globalObjsString.Substring(0, secondDotIndex)]; propname = globalObjsString.Substring(secondDotIndex + 1); } else { UberTreeParser.global_objs[globalObjsString] = GetReturnObject(Children[1], trigObject); return(ProcessResult.None); } } } else if (scriptString.StartsWith("ints.")) { value = ((MathTree)Children[1]).Calculate(trigObject); switch (AssignType) { case AssignmentType.Plus: trigObject.ints[scriptString.Substring(5)] = trigObject.ints[scriptString.Substring(5)] + Convert.ToInt32(value); break; case AssignmentType.Minus: trigObject.ints[scriptString.Substring(5)] = trigObject.ints[scriptString.Substring(5)] - Convert.ToInt32(value); break; case AssignmentType.Mult: trigObject.ints[scriptString.Substring(5)] = trigObject.ints[scriptString.Substring(5)] * Convert.ToInt32(value); break; case AssignmentType.Divide: trigObject.ints[scriptString.Substring(5)] = Convert.ToInt32(trigObject.ints[scriptString.Substring(5)] / Convert.ToDouble(value)); break; default: trigObject.ints[scriptString.Substring(5)] = Convert.ToInt32(value); break; } return(ProcessResult.None); } else if (scriptString.StartsWith("strings.")) { value = GetReturnObject(Children[1], trigObject); if (value != null) { value = value.ToString(); switch (AssignType) { case AssignmentType.Plus: trigObject.strings[scriptString.Substring(8)] = trigObject.strings[scriptString.Substring(8)] + (string)value; break; case AssignmentType.Regular: trigObject.strings[scriptString.Substring(8)] = (string)value; break; default: throw new UberScriptException( "Line " + LineNumber + ": " + ScriptString + "\nTried to use +=, -=, *=, or /= but this is only supported for " + value.GetType() + "! It is only supported for int, double, and UInt64"); } return(ProcessResult.None); } throw new UberScriptException( "Line " + LineNumber + ": " + ScriptString + "\nTried to add null value to strings dictionary... this is not allowed!"); } else if (scriptString.StartsWith("doubles.")) { value = ((MathTree)Children[1]).Calculate(trigObject); switch (AssignType) { case AssignmentType.Plus: trigObject.doubles[scriptString.Substring(8)] = trigObject.doubles[scriptString.Substring(8)] + Convert.ToDouble(value); break; case AssignmentType.Minus: trigObject.doubles[scriptString.Substring(8)] = trigObject.doubles[scriptString.Substring(8)] - Convert.ToDouble(value); break; case AssignmentType.Mult: trigObject.doubles[scriptString.Substring(8)] = trigObject.doubles[scriptString.Substring(8)] * Convert.ToDouble(value); break; case AssignmentType.Divide: trigObject.doubles[scriptString.Substring(8)] = trigObject.doubles[scriptString.Substring(8)] / Convert.ToDouble(value); break; default: trigObject.doubles[scriptString.Substring(8)] = Convert.ToDouble(value); break; } return(ProcessResult.None); } else if (scriptString.StartsWith("objs.")) { localObjsString = scriptString.Substring(5); int secondDotIndex = localObjsString.IndexOf('.'); if (secondDotIndex > -1) // we are trying to set a property on an existing object { if (!trigObject.objs.TryGetValue(localObjsString.Substring(0, secondDotIndex), out o)) { // will throw error if it doesn't exist yet throw new UberScriptException( "local objs dictionary did not have entry: " + localObjsString.Substring(0, secondDotIndex)); } propname = localObjsString.Substring(secondDotIndex + 1); } else { trigObject.objs[localObjsString] = GetReturnObject(Children[1], trigObject); return(ProcessResult.None); } } } // only makes it here if we aren't editing global variables directly // (or if the we are returning a global object, e.g. global_objs.goober.hits if (o == null) { o = trigObject.Spawn ?? trigObject.This; propname = scriptString; } } // 2nd child is the value, but could have an operation in it and needs to be parsed try { if (AssignType == AssignmentType.Regular) { value = GetReturnObject(Children[1], trigObject); } else { Type ptype; value = PropertyGetters.GetObject(trigObject, o, propname, out ptype); if (value is int) { switch (AssignType) { case AssignmentType.Plus: value = (int)value + Convert.ToInt32(GetReturnObject(Children[1], trigObject)); break; case AssignmentType.Minus: value = (int)value - Convert.ToInt32(GetReturnObject(Children[1], trigObject)); break; case AssignmentType.Mult: value = Convert.ToInt32((int)value * Convert.ToDouble(GetReturnObject(Children[1], trigObject))); break; case AssignmentType.Divide: value = Convert.ToInt32((int)value / Convert.ToDouble(GetReturnObject(Children[1], trigObject))); break; } } else if (value is double) { switch (AssignType) { case AssignmentType.Plus: value = (double)value + Convert.ToDouble(GetReturnObject(Children[1], trigObject)); break; case AssignmentType.Minus: value = (double)value - Convert.ToDouble(GetReturnObject(Children[1], trigObject)); break; case AssignmentType.Mult: value = (double)value * Convert.ToDouble(GetReturnObject(Children[1], trigObject)); break; case AssignmentType.Divide: value = (double)value / Convert.ToDouble(GetReturnObject(Children[1], trigObject)); break; } } else if (value is UInt64) { switch (AssignType) { case AssignmentType.Plus: value = (UInt64)value + Convert.ToUInt64(GetReturnObject(Children[1], trigObject)); break; case AssignmentType.Minus: value = (UInt64)value - Convert.ToUInt64(GetReturnObject(Children[1], trigObject)); break; case AssignmentType.Mult: value = Convert.ToUInt64((UInt64)value * Convert.ToDouble(GetReturnObject(Children[1], trigObject))); break; case AssignmentType.Divide: value = Convert.ToUInt64((UInt64)value / Convert.ToDouble(GetReturnObject(Children[1], trigObject))); break; } } else if (value is string) { if (AssignType == AssignmentType.Plus) { value = (string)value + GetReturnObject(Children[1], trigObject); } else { throw new UberScriptException( "Tried to use +=, -=, *=, or /= but this is only supported for " + value.GetType() + "! It is only supported for int, double, and UInt64"); } } else { //throw new UberScriptException("Tried to use +=, -=, *=, or /= on a non-numeric left-hand side!"); throw new UberScriptException( "Tried to use +=, -=, *=, or /= but this is only supported for " + value.GetType() + "! It is only supported for int, double, and UInt64"); } } } catch (Exception e) { throw new UberScriptException("Line " + LineNumber + ": " + ScriptString + "\nGetReturnObject Error:", e); } try { PropertySetters.SetPropertyValue(trigObject, o, propname, value); } catch (Exception e) { throw new UberScriptException("Line " + LineNumber + ": " + ScriptString + "\nError setting value:", e); } return(ProcessResult.None); }