static MethodResult GetMapID(bool isGlobal, VarCollection varCollection, object[] parameters) { /* * All credit to https://us.forums.blizzard.com/en/overwatch/t/workshop-resource-get-the-current-map-name-updated-1-action/ * Based off code: 5VAQA */ int mapcount = 0; for (int i = 0; i < Constants.MapChecks.Length; i++) { mapcount += Constants.MapChecks[i].Length; } V_Append prev = null; V_Append current = null; for (int s = 0; s < Constants.MapChecks.Length; s++) { for (int i = 0; i < Constants.MapChecks[s].Length; i++) { current = new V_Append() { ParameterValues = new IWorkshopTree[2] }; if (prev != null) { current.ParameterValues[0] = prev; } else { current.ParameterValues[0] = new V_EmptyArray(); } // Set the map ID current.ParameterValues[1] = new V_Number(Constants.MapChecks[s][i]); prev = current; } } return(new MethodResult(null, Element.Part <V_IndexOfArrayValue>(current, Element.Part <V_RoundToInteger>(Element.Part <V_Divide>( Element.Part <V_RoundToInteger>( Element.Part <V_Multiply>( Element.Part <V_DistanceBetween>( Element.Part <V_Vector>(new V_Number(0), new V_Number(0), new V_Number(0)), Element.Part <V_NearestWalkablePosition>(Element.Part <V_Vector>(new V_Number(100), new V_Number(100), new V_Number(100))) ), new V_Number(100) ), EnumData.GetEnumValue(Rounding.Down) ), new V_Number(4) ), EnumData.GetEnumValue(Rounding.Down)) ), CustomMethodType.Value)); }
Element ParseExpression(DeltinScriptParser.ExprContext context) { // If the expression is a(n)... #region Operation // 0 1 2 // (expr operation expr) // count == 3 if (context.ChildCount == 3 && (Constants.MathOperations.Contains(context.GetChild(1).GetText()) || Constants.CompareOperations.Contains(context.GetChild(1).GetText()) || Constants.BoolOperations.Contains(context.GetChild(1).GetText()))) { Element left = ParseExpression(context.GetChild(0) as DeltinScriptParser.ExprContext); string operation = context.GetChild(1).GetText(); Element right = ParseExpression(context.GetChild(2) as DeltinScriptParser.ExprContext); if (Constants.BoolOperations.Contains(context.GetChild(1).GetText())) { if (left.ElementData.ValueType != Elements.ValueType.Any && left.ElementData.ValueType != Elements.ValueType.Boolean) { throw new SyntaxErrorException($"Expected boolean datatype, got {left .ElementData.ValueType.ToString()} instead.", context.start); } if (right.ElementData.ValueType != Elements.ValueType.Any && right.ElementData.ValueType != Elements.ValueType.Boolean) { throw new SyntaxErrorException($"Expected boolean datatype, got {right.ElementData.ValueType.ToString()} instead.", context.start); } } switch (operation) { case "^": return(Element.Part <V_RaiseToPower>(left, right)); case "*": return(Element.Part <V_Multiply>(left, right)); case "/": return(Element.Part <V_Divide>(left, right)); case "+": return(Element.Part <V_Add>(left, right)); case "-": return(Element.Part <V_Subtract>(left, right)); case "%": return(Element.Part <V_Modulo>(left, right)); // COMPARE : '<' | '<=' | '==' | '>=' | '>' | '!='; case "&": return(Element.Part <V_And>(left, right)); case "|": return(Element.Part <V_Or>(left, right)); case "<": return(Element.Part <V_Compare>(left, Operators.LessThan, right)); case "<=": return(Element.Part <V_Compare>(left, Operators.LessThanOrEqual, right)); case "==": return(Element.Part <V_Compare>(left, Operators.Equal, right)); case ">=": return(Element.Part <V_Compare>(left, Operators.GreaterThanOrEqual, right)); case ">": return(Element.Part <V_Compare>(left, Operators.GreaterThan, right)); case "!=": return(Element.Part <V_Compare>(left, Operators.NotEqual, right)); } } #endregion #region Not if (context.GetChild(0) is DeltinScriptParser.NotContext) { return(Element.Part <V_Not>(ParseExpression(context.GetChild(1) as DeltinScriptParser.ExprContext))); } #endregion #region Number if (context.GetChild(0) is DeltinScriptParser.NumberContext) { var number = context.GetChild(0); double num = double.Parse(number.GetChild(0).GetText()); /* * // num will have the format expr(number(X)) if positive, expr(number(neg(X))) if negative. * if (number.GetChild(0) is DeltinScriptParser.NegContext) * // Is negative, use '-' before int.parse to make it negative. * num = -double.Parse(number.GetChild(0).GetText()); * else * // Is positive * num = double.Parse(number.GetChild(0).GetText()); */ return(new V_Number(num)); } #endregion #region Boolean // True if (context.GetChild(0) is DeltinScriptParser.TrueContext) { return(new V_True()); } // False if (context.GetChild(0) is DeltinScriptParser.FalseContext) { return(new V_False()); } #endregion #region String if (context.GetChild(0) is DeltinScriptParser.StringContext) { return(V_String.ParseString( context.start, // String will look like "hey this is the contents", trim the quotes. (context.GetChild(0) as DeltinScriptParser.StringContext).STRINGLITERAL().GetText().Trim('\"'), null )); } #endregion #region Formatted String if (context.GetChild(1) is DeltinScriptParser.StringContext) { Element[] values = context.expr().Select(expr => ParseExpression(expr)).ToArray(); return(V_String.ParseString( context.start, (context.GetChild(1) as DeltinScriptParser.StringContext).STRINGLITERAL().GetText().Trim('\"'), values )); } #endregion #region null if (context.GetChild(0) is DeltinScriptParser.NullContext) { return(new V_Null()); } #endregion #region Group ( expr ) if (context.ChildCount == 3 && context.GetChild(0).GetText() == "(" && context.GetChild(1) is DeltinScriptParser.ExprContext && context.GetChild(2).GetText() == ")") { Console.WriteLine("Group type:" + context.GetChild(0).GetType()); return(ParseExpression(context.GetChild(1) as DeltinScriptParser.ExprContext)); } #endregion #region Method if (context.GetChild(0) is DeltinScriptParser.MethodContext) { return(ParseMethod(context.GetChild(0) as DeltinScriptParser.MethodContext, true)); } #endregion #region Variable if (context.GetChild(0) is DeltinScriptParser.VariableContext) { return(DefinedVar.GetVar((context.GetChild(0) as DeltinScriptParser.VariableContext).PART().GetText(), context.start).GetVariable(new V_EventPlayer())); } #endregion #region Array if (context.ChildCount == 4 && context.GetChild(1).GetText() == "[" && context.GetChild(3).GetText() == "]") { return(Element.Part <V_ValueInArray>( ParseExpression(context.expr(0) as DeltinScriptParser.ExprContext), ParseExpression(context.expr(1) as DeltinScriptParser.ExprContext))); } #endregion #region Create Array if (context.ChildCount >= 4 && context.GetChild(0).GetText() == "[") { var expressions = context.expr(); V_Append prev = null; V_Append current = null; for (int i = 0; i < expressions.Length; i++) { current = new V_Append() { ParameterValues = new object[2] }; if (prev != null) { current.ParameterValues[0] = prev; } else { current.ParameterValues[0] = new V_EmptyArray(); } current.ParameterValues[1] = ParseExpression(expressions[i]); prev = current; } return(current); } #endregion #region Empty Array if (context.ChildCount == 2 && context.GetText() == "[]") { return(Element.Part <V_EmptyArray>()); } #endregion #region Seperator/enum if (context.ChildCount == 3 && context.GetChild(1).GetText() == ".") { Element left = ParseExpression(context.GetChild(0) as DeltinScriptParser.ExprContext); string variableName = context.GetChild(2).GetChild(0).GetText(); DefinedVar var = DefinedVar.GetVar(variableName, context.start); return(var.GetVariable(left)); } #endregion throw new Exception($"What's a {context.GetType().Name}?"); }
private Translate(RuleNode ruleNode, ScopeGroup root, VarCollection varCollection, UserMethod[] userMethods) { Root = root; VarCollection = varCollection; UserMethods = userMethods; Rule = new Rule(ruleNode.Name, ruleNode.Event, ruleNode.Team, ruleNode.Player); IsGlobal = Rule.IsGlobal; ContinueSkip = new ContinueSkip(IsGlobal, Actions, varCollection); ParseConditions(ruleNode.Conditions); ParseBlock(root.Child(), ruleNode.Block, false, null); Rule.Actions = Actions.ToArray(); Rule.Conditions = Conditions.ToArray(); // Fufill remaining skips foreach (var skip in ReturnSkips) { skip.ParameterValues = new IWorkshopTree[] { new V_Number(Actions.Count - ReturnSkips.IndexOf(skip)) } } ; ReturnSkips.Clear(); } void ParseConditions(IExpressionNode[] expressions) { foreach (var expr in expressions) { Element parsedIf = ParseExpression(Root, expr); // If the parsed if is a V_Compare, translate it to a condition. // Makes "(value1 == value2) == true" to just "value1 == value2" if (parsedIf is V_Compare) { Element left = (Element)parsedIf.ParameterValues[0]; if (!left.ElementData.IsValue) { throw SyntaxErrorException.InvalidMethodType(true, left.Name, ((Node)expr).Range); } Element right = (Element)parsedIf.ParameterValues[2]; if (!right.ElementData.IsValue) { throw SyntaxErrorException.InvalidMethodType(true, right.Name, ((Node)expr).Range); } Conditions.Add( new Condition( left, (EnumMember)parsedIf.ParameterValues[1], right ) ); } // If not, just do "parsedIf == true" else { if (!parsedIf.ElementData.IsValue) { throw SyntaxErrorException.InvalidMethodType(true, parsedIf.Name, ((Node)expr).Range); } Conditions.Add(new Condition( parsedIf, EnumData.GetEnumValue(Operators.Equal), new V_True() )); } } } Element ParseExpression(ScopeGroup scope, IExpressionNode expression) { switch (expression) { // Math and boolean operations. case OperationNode operationNode: { Element left = ParseExpression(scope, operationNode.Left); Element right = ParseExpression(scope, operationNode.Right); /* * if (Constants.BoolOperations.Contains(operationNode.Operation)) * { * if (left.ElementData.ValueType != Elements.ValueType.Any && left.ElementData.ValueType != Elements.ValueType.Boolean) * throw new SyntaxErrorException($"Expected boolean, got {left .ElementData.ValueType.ToString()} instead.", ((Node)operationNode.Left).Range); * * if (right.ElementData.ValueType != Elements.ValueType.Any && right.ElementData.ValueType != Elements.ValueType.Boolean) * throw new SyntaxErrorException($"Expected boolean, got {right.ElementData.ValueType.ToString()} instead.", ((Node)operationNode.Right).Range); * } */ switch (operationNode.Operation) { // Math: ^, *, %, /, +, - case "^": return(Element.Part <V_RaiseToPower>(left, right)); case "*": return(Element.Part <V_Multiply>(left, right)); case "%": return(Element.Part <V_Modulo>(left, right)); case "/": return(Element.Part <V_Divide>(left, right)); case "+": return(Element.Part <V_Add>(left, right)); case "-": return(Element.Part <V_Subtract>(left, right)); // BoolCompare: &, | case "&": return(Element.Part <V_And>(left, right)); case "|": return(Element.Part <V_Or>(left, right)); // Compare: <, <=, ==, >=, >, != case "<": return(Element.Part <V_Compare>(left, EnumData.GetEnumValue(Operators.LessThan), right)); case "<=": return(Element.Part <V_Compare>(left, EnumData.GetEnumValue(Operators.LessThanOrEqual), right)); case "==": return(Element.Part <V_Compare>(left, EnumData.GetEnumValue(Operators.Equal), right)); case ">=": return(Element.Part <V_Compare>(left, EnumData.GetEnumValue(Operators.GreaterThanOrEqual), right)); case ">": return(Element.Part <V_Compare>(left, EnumData.GetEnumValue(Operators.GreaterThan), right)); case "!=": return(Element.Part <V_Compare>(left, EnumData.GetEnumValue(Operators.NotEqual), right)); } throw new Exception($"Operation {operationNode.Operation} not implemented."); } // Number case NumberNode numberNode: return(new V_Number(numberNode.Value)); // Bool case BooleanNode boolNode: if (boolNode.Value) { return(new V_True()); } else { return(new V_False()); } // Not operation case NotNode notNode: return(Element.Part <V_Not>(ParseExpression(scope, notNode.Value))); // Strings case StringNode stringNode: Element[] stringFormat = new Element[stringNode.Format?.Length ?? 0]; for (int i = 0; i < stringFormat.Length; i++) { stringFormat[i] = ParseExpression(scope, stringNode.Format[i]); } return(V_String.ParseString(stringNode.Range, stringNode.Value, stringFormat)); // Null case NullNode nullNode: return(new V_Null()); // TODO check if groups need to be implemented here // Methods case MethodNode methodNode: return(ParseMethod(scope, methodNode, true)); // Variable case VariableNode variableNode: return(scope.GetVar(variableNode.Name, variableNode.Range, Diagnostics) .GetVariable(variableNode.Target != null ? ParseExpression(scope, variableNode.Target) : null)); // Get value in array case ValueInArrayNode viaNode: return(Element.Part <V_ValueInArray>(ParseExpression(scope, viaNode.Value), ParseExpression(scope, viaNode.Index))); // Create array case CreateArrayNode createArrayNode: { Element prev = null; Element current = null; for (int i = 0; i < createArrayNode.Values.Length; i++) { current = new V_Append() { ParameterValues = new IWorkshopTree[2] }; if (prev != null) { current.ParameterValues[0] = prev; } else { current.ParameterValues[0] = new V_EmptyArray(); } current.ParameterValues[1] = ParseExpression(scope, createArrayNode.Values[i]); prev = current; } return(current ?? new V_EmptyArray()); } case EnumNode enumNode: return(EnumData.Special(enumNode.EnumMember) ?? throw SyntaxErrorException.EnumCantBeValue(enumNode.Type, enumNode.Range)); // Seperator } throw new Exception(); } Element ParseMethod(ScopeGroup scope, MethodNode methodNode, bool needsToBeValue) { methodNode.RelatedScopeGroup = scope; // Get the kind of method the method is (Method (Overwatch), Custom Method, or User Method.) var methodType = GetMethodType(UserMethods, methodNode.Name); // Throw exception if the method does not exist. if (methodType == null) { throw SyntaxErrorException.NonexistentMethod(methodNode.Name, methodNode.Range); } Element method; switch (methodType) { case MethodType.Method: { Type owMethod = Element.GetMethod(methodNode.Name); method = (Element)Activator.CreateInstance(owMethod); Parameter[] parameterData = owMethod.GetCustomAttributes <Parameter>().ToArray(); List <IWorkshopTree> parsedParameters = new List <IWorkshopTree>(); for (int i = 0; i < parameterData.Length; i++) { if (methodNode.Parameters.Length > i) { // Parse the parameter. parsedParameters.Add(ParseParameter(scope, methodNode.Parameters[i], methodNode.Name, parameterData[i])); } else { if (parameterData[i].ParameterType == ParameterType.Value && parameterData[i].DefaultType == null) { // Throw exception if there is no default method to fallback on. throw SyntaxErrorException.MissingParameter(parameterData[i].Name, methodNode.Name, methodNode.Range); } else { parsedParameters.Add(parameterData[i].GetDefault()); } } } method.ParameterValues = parsedParameters.ToArray(); break; } case MethodType.CustomMethod: { MethodInfo customMethod = CustomMethods.GetCustomMethod(methodNode.Name); Parameter[] parameterData = customMethod.GetCustomAttributes <Parameter>().ToArray(); object[] parsedParameters = new Element[parameterData.Length]; for (int i = 0; i < parameterData.Length; i++) { if (methodNode.Parameters.Length > i) { parsedParameters[i] = ParseParameter(scope, methodNode.Parameters[i], methodNode.Name, parameterData[i]); } else { // Throw exception if there is no default method to fallback on. throw SyntaxErrorException.MissingParameter(parameterData[i].Name, methodNode.Name, methodNode.Range); } } MethodResult result = (MethodResult)customMethod.Invoke(null, new object[] { IsGlobal, VarCollection, parsedParameters }); switch (result.MethodType) { case CustomMethodType.Action: if (needsToBeValue) { throw SyntaxErrorException.InvalidMethodType(true, methodNode.Name, methodNode.Range); } break; case CustomMethodType.MultiAction_Value: case CustomMethodType.Value: if (!needsToBeValue) { throw SyntaxErrorException.InvalidMethodType(false, methodNode.Name, methodNode.Range); } break; } // Some custom methods have extra actions. if (result.Elements != null) { Actions.AddRange(result.Elements); } method = result.Result; break; } case MethodType.UserMethod: { if (!AllowRecursion) { UserMethod userMethod = UserMethod.GetUserMethod(UserMethods, methodNode.Name); if (MethodStackNoRecursive.Contains(userMethod)) { throw SyntaxErrorException.RecursionNotAllowed(methodNode.Range); } var methodScope = Root.Child(); // Add the parameter variables to the scope. DefinedVar[] parameterVars = new DefinedVar[userMethod.Parameters.Length]; for (int i = 0; i < parameterVars.Length; i++) { if (methodNode.Parameters.Length > i) { // Create a new variable using the parameter input. parameterVars[i] = VarCollection.AssignDefinedVar(methodScope, IsGlobal, userMethod.Parameters[i].Name, methodNode.Range); Actions.Add(parameterVars[i].SetVariable(ParseExpression(scope, methodNode.Parameters[i]))); } else { throw SyntaxErrorException.MissingParameter(userMethod.Parameters[i].Name, methodNode.Name, methodNode.Range); } } var returns = VarCollection.AssignVar($"{methodNode.Name}: return temp value", IsGlobal); MethodStackNoRecursive.Add(userMethod); var userMethodScope = methodScope.Child(); userMethod.Block.RelatedScopeGroup = userMethodScope; ParseBlock(userMethodScope, userMethod.Block, true, returns); MethodStackNoRecursive.Remove(userMethod); // No return value if the method is being used as an action. if (needsToBeValue) { method = returns.GetVariable(); } else { method = null; } break; } else { UserMethod userMethod = UserMethod.GetUserMethod(UserMethods, methodNode.Name); MethodStack lastMethod = MethodStack.FirstOrDefault(ms => ms.UserMethod == userMethod); if (lastMethod != null) { ContinueSkip.Setup(); for (int i = 0; i < lastMethod.ParameterVars.Length; i++) { if (methodNode.Parameters.Length > i) { Actions.Add(lastMethod.ParameterVars[i].Push(ParseExpression(scope, methodNode.Parameters[i]))); } } // ?--- Multidimensional Array Actions.Add( Element.Part <A_SetGlobalVariable>(EnumData.GetEnumValue(Variable.B), lastMethod.ContinueSkipArray.GetVariable()) ); Actions.Add( Element.Part <A_ModifyGlobalVariable>(EnumData.GetEnumValue(Variable.B), EnumData.GetEnumValue(Operation.AppendToArray), new V_Number(ContinueSkip.GetSkipCount() + 4)) ); Actions.Add( lastMethod.ContinueSkipArray.SetVariable(Element.Part <V_GlobalVariable>(EnumData.GetEnumValue(Variable.B))) ); // ?--- ContinueSkip.SetSkipCount(lastMethod.ActionIndex); Actions.Add(Element.Part <A_Loop>()); if (needsToBeValue) { method = lastMethod.Return.GetVariable(); } else { method = null; } } else { var methodScope = Root.Child(); // Add the parameter variables to the scope. ParameterVar[] parameterVars = new ParameterVar[userMethod.Parameters.Length]; for (int i = 0; i < parameterVars.Length; i++) { if (methodNode.Parameters.Length > i) { // Create a new variable using the parameter input. parameterVars[i] = VarCollection.AssignParameterVar(Actions, methodScope, IsGlobal, userMethod.Parameters[i].Name, methodNode.Range); Actions.Add(parameterVars[i].Push(ParseExpression(scope, methodNode.Parameters[i]))); } else { throw SyntaxErrorException.MissingParameter(userMethod.Parameters[i].Name, methodNode.Name, methodNode.Range); } } var returns = VarCollection.AssignVar($"{methodNode.Name}: return temp value", IsGlobal); Var continueSkipArray = VarCollection.AssignVar($"{methodNode.Name}: continue skip temp value", IsGlobal); var stack = new MethodStack(userMethod, parameterVars, ContinueSkip.GetSkipCount(), returns, continueSkipArray); MethodStack.Add(stack); var userMethodScope = methodScope.Child(); userMethod.Block.RelatedScopeGroup = userMethodScope; ParseBlock(userMethodScope, userMethod.Block, true, returns); // No return value if the method is being used as an action. if (needsToBeValue) { method = returns.GetVariable(); } else { method = null; } Actions.Add(Element.Part <A_Wait>(new V_Number(Constants.MINIMUM_WAIT))); for (int i = 0; i < parameterVars.Length; i++) { parameterVars[i].Pop(); } ContinueSkip.Setup(); ContinueSkip.SetSkipCount(Element.Part <V_LastOf>(continueSkipArray.GetVariable())); // ?--- Multidimensional Array Actions.Add( Element.Part <A_SetGlobalVariable>(EnumData.GetEnumValue(Variable.B), continueSkipArray.GetVariable()) ); Actions.Add( continueSkipArray.SetVariable( Element.Part <V_ArraySlice>( Element.Part <V_GlobalVariable>(EnumData.GetEnumValue(Variable.B)), new V_Number(0), Element.Part <V_Subtract>( Element.Part <V_CountOf>(Element.Part <V_GlobalVariable>(EnumData.GetEnumValue(Variable.B))), new V_Number(1) ) ) ) ); // ?--- Actions.Add( Element.Part <A_LoopIf>( Element.Part <V_Compare>( Element.Part <V_CountOf>(continueSkipArray.GetVariable()), EnumData.GetEnumValue(Operators.NotEqual), new V_Number(0) ) ) ); ContinueSkip.ResetSkip(); MethodStack.Remove(stack); } break; } } default: throw new NotImplementedException(); } methodNode.RelatedElement = method; return(method); } IWorkshopTree ParseParameter(ScopeGroup scope, IExpressionNode node, string methodName, Parameter parameterData) { IWorkshopTree value = null; switch (node) { case EnumNode enumNode: /* * if (parameterData.ParameterType != ParameterType.Enum) * throw SyntaxErrorException.ExpectedType(true, parameterData.ValueType.ToString(), methodName, parameterData.Name, enumNode.Range); * * if (enumNode.Type != parameterData.EnumType.Name) * throw SyntaxErrorException.ExpectedType(false, parameterData.EnumType.ToString(), methodName, parameterData.Name, enumNode.Range); */ value = (IWorkshopTree)EnumData.Special(enumNode.EnumMember) ?? (IWorkshopTree)enumNode.EnumMember; //if (value == null) // throw SyntaxErrorException.InvalidEnumValue(enumNode.Type, enumNode.Value, enumNode.Range); break; default: if (parameterData.ParameterType != ParameterType.Value) { throw SyntaxErrorException.ExpectedType(false, parameterData.EnumType.Name, methodName, parameterData.Name, ((Node)node).Range); } value = ParseExpression(scope, node); Element element = value as Element; ElementData elementData = element.GetType().GetCustomAttribute <ElementData>(); if (elementData.ValueType != Elements.ValueType.Any && !parameterData.ValueType.HasFlag(elementData.ValueType)) { throw SyntaxErrorException.InvalidType(parameterData.ValueType, elementData.ValueType, ((Node)node).Range); } break; } if (value == null) { throw new Exception("Failed to parse parameter."); } return(value); }
public Element ParseExpression(ScopeGroup getter, ScopeGroup scope, Node expression) { switch (expression) { // Math and boolean operations. case OperationNode operationNode: { Element left = ParseExpression(getter, scope, operationNode.Left); Element right = ParseExpression(getter, scope, operationNode.Right); /* * if (Constants.BoolOperations.Contains(operationNode.Operation)) * { * if (left.ElementData.ValueType != Elements.ValueType.Any && left.ElementData.ValueType != Elements.ValueType.Boolean) * throw new SyntaxErrorException($"Expected boolean, got {left .ElementData.ValueType.ToString()} instead.", ((Node)operationNode.Left).Range); * * if (right.ElementData.ValueType != Elements.ValueType.Any && right.ElementData.ValueType != Elements.ValueType.Boolean) * throw new SyntaxErrorException($"Expected boolean, got {right.ElementData.ValueType.ToString()} instead.", ((Node)operationNode.Right).Range); * } */ switch (operationNode.Operation) { // Math: ^, *, %, /, +, - case "^": return(Element.Part <V_RaiseToPower>(left, right)); case "*": return(left * right); case "%": return(left % right); case "/": return(left / right); case "+": return(left + right); case "-": return(left - right); // BoolCompare: &&, || case "&&": return(Element.Part <V_And>(left, right)); case "||": return(Element.Part <V_Or>(left, right)); // Compare: <, <=, ==, >=, >, != case "<": return(left < right); case "<=": return(left <= right); case "==": return(Element.Part <V_Compare>(left, EnumData.GetEnumValue(Operators.Equal), right)); case ">=": return(left >= right); case ">": return(left > right); case "!=": return(Element.Part <V_Compare>(left, EnumData.GetEnumValue(Operators.NotEqual), right)); } throw new Exception($"Operation {operationNode.Operation} not implemented."); } // Number case NumberNode numberNode: return(numberNode.Value); // Bool case BooleanNode boolNode: if (boolNode.Value) { return(new V_True()); } else { return(new V_False()); } // Not operation case NotNode notNode: return(!(ParseExpression(getter, scope, notNode.Value))); case InvertNode invertNode: return(-ParseExpression(getter, scope, invertNode.Value)); // Strings case StringNode stringNode: Element[] stringFormat = new Element[stringNode.Format?.Length ?? 0]; for (int i = 0; i < stringFormat.Length; i++) { stringFormat[i] = ParseExpression(getter, scope, stringNode.Format[i]); } if (stringNode.Localized) { return(V_String.ParseString(stringNode.Location, stringNode.Value, stringFormat)); } else { return(V_CustomString.ParseString(stringNode.Location, stringNode.Value, stringFormat)); } // Null case NullNode nullNode: return(new V_Null()); // TODO check if groups need to be implemented here // Methods case MethodNode methodNode: return(ParseMethod(getter, scope, methodNode, true)); // Variable case VariableNode variableNode: Element[] index = new Element[variableNode.Index.Length]; for (int i = 0; i < index.Length; i++) { index[i] = ParseExpression(getter, scope, variableNode.Index[i]); } Var var = scope.GetVar(getter, variableNode.Name, variableNode.Location); if (!var.Gettable()) { throw SyntaxErrorException.VariableIsReadonly(var.Name, variableNode.Location); } Element result = var.GetVariable(); for (int i = 0; i < index.Length; i++) { result = Element.Part <V_ValueInArray>(result, index[i]); } return(result); // Get value in array case ValueInArrayNode viaNode: return(Element.Part <V_ValueInArray>(ParseExpression(getter, scope, viaNode.Value), ParseExpression(getter, scope, viaNode.Index))); // Create array case CreateArrayNode createArrayNode: { Element prev = null; Element current = null; for (int i = 0; i < createArrayNode.Values.Length; i++) { current = new V_Append() { ParameterValues = new IWorkshopTree[2] }; if (prev != null) { current.ParameterValues[0] = prev; } else { current.ParameterValues[0] = new V_EmptyArray(); } current.ParameterValues[1] = ParseExpression(getter, scope, createArrayNode.Values[i]); prev = current; } return(current ?? new V_EmptyArray()); } // Ternary Conditional (a ? b : c) case TernaryConditionalNode ternaryNode: return(Element.TernaryConditional ( ParseExpression(getter, scope, ternaryNode.Condition), ParseExpression(getter, scope, ternaryNode.Consequent), ParseExpression(getter, scope, ternaryNode.Alternative) )); // Enums case EnumNode enumNode: return(EnumData.ToElement(enumNode.EnumMember) ?? throw SyntaxErrorException.EnumCantBeValue(enumNode.Type, enumNode.Location)); // New object case CreateObjectNode createObjectNode: DefinedType typeData = ParserData.GetDefinedType(createObjectNode.TypeName, createObjectNode.Location); return(typeData.New(createObjectNode, getter, scope, this)); // Expression tree case ExpressionTreeNode expressionTree: return(new ParseExpressionTree(this, getter, scope, expressionTree).ResultingElement); // This case ThisNode thisNode: return(getter.GetThis(thisNode.Location)); // Type convert case TypeConvertNode typeConvertNode: DefinedType type = ParserData.GetDefinedType(typeConvertNode.Type, typeConvertNode.Location); Element element = ParseExpression(getter, scope, typeConvertNode.Expression); type.GetSource(this, element, typeConvertNode.Location); return(element); case RootNode rootNode: throw new SyntaxErrorException("'root' cannot be used like an expression.", rootNode.Location); } throw new Exception(); }