private void GetConditions(RuleAction ruleAction) { foreach (var condition in ruleAction.Conditions) { var conditionParse = condition.Expression.Parse(ActionSet); Element value1; EnumMember compareOperator; Element value2; if (conditionParse is V_Compare) { value1 = (Element)((Element)conditionParse).ParameterValues[0]; compareOperator = (EnumMember)((Element)conditionParse).ParameterValues[1]; value2 = (Element)((Element)conditionParse).ParameterValues[2]; } else { value1 = (Element)conditionParse; compareOperator = EnumData.GetEnumValue(Operators.Equal); value2 = new V_True(); } Conditions.Add(new Condition(value1, compareOperator, value2)); } }
public void Setup(ActionSet actionSet) { if (IsSetup) { return; } IsSetup = true; SkipCount = Rule.DeltinScript.VarCollection.Assign("continueSkip", Rule.IsGlobal, true); TempHolder = Rule.DeltinScript.VarCollection.Assign("continueSkipTemp", Rule.IsGlobal, true); A_SkipIf skipAction = Element.Part <A_SkipIf> ( // Condition Element.Part <V_Compare>(SkipCount.GetVariable(), EnumData.GetEnumValue(Operators.Equal), new V_Number(0)), // Number of actions new V_Number(3) ); Skipper = new SkipStartMarker(actionSet); Skipper.SkipCount = TempHolder.GetVariable(); IActionList[] actions = ArrayBuilder <IActionList> .Build( new ALAction(A_Wait.MinimumWait), new ALAction(skipAction), new ALAction(TempHolder.SetVariable((Element)SkipCount.GetVariable())[0]), new ALAction(SkipCount.SetVariable(0)[0]), Skipper ); Rule.Actions.InsertRange(0, actions); }
public Element GetArray() { return(Element.CreateArray( // Convert the maps to EnumMembers encased in V_MapVar. Maps.Select(m => Element.Part <V_MapVar>(EnumData.GetEnumValue(m))) .ToArray() )); }
public Var(string name, bool isGlobal, Variable variable, int index) { Name = name; IsGlobal = isGlobal; Variable = variable; VariableAsWorkshop = EnumData.GetEnumValue(Variable); Index = index; }
private Rule GetStartRule(DeltinScript deltinScript) { var condition = new Condition( Element.Part <V_CountOf>(Path.GetVariable()), Operators.GreaterThan, 0 ); Element eventPlayer = new V_EventPlayer(); Element eventPlayerPos = Element.Part <V_PositionOf>(eventPlayer); TranslateRule rule = new TranslateRule(deltinScript, Constants.INTERNAL_ELEMENT + "Pathfinder: Move", RuleEvent.OngoingPlayer); IfBuilder isBetween = new IfBuilder(rule.ActionSet, Element.Part <V_And>( Element.Part <V_CountOf>(Path.GetVariable()) >= 2, IsBetween(eventPlayerPos, NextPosition(eventPlayer), PositionAt(eventPlayer, 1)) ) ); isBetween.Setup(); rule.ActionSet.AddAction(Next()); isBetween.Finish(); rule.ActionSet.AddAction(ArrayBuilder <Element> .Build ( LastUpdate.SetVariable(new V_TotalTimeElapsed()), DistanceToNext.SetVariable(Element.Part <V_DistanceBetween>(Element.Part <V_PositionOf>(new V_EventPlayer()), NextPosition(new V_EventPlayer()))), // Element.Part<A_StartFacing>( // new V_EventPlayer(), // Element.Part<V_DirectionTowards>( // new V_EyePosition(), // NextPosition() // ), // new V_Number(700), // EnumData.GetEnumValue(Relative.ToWorld), // EnumData.GetEnumValue(FacingRev.DirectionAndTurnRate) // ), // Move to the next node. Element.Part <A_StartThrottleInDirection>( new V_EventPlayer(), Element.Part <V_DirectionTowards>( new V_EyePosition(), NextPosition(new V_EventPlayer()) // Because of ThrottleRev this will be reevaluated so 'Start Throttle In Direction' only needs to run once. ), new V_Number(1), EnumData.GetEnumValue(Relative.ToWorld), EnumData.GetEnumValue(ThrottleBehavior.ReplaceExistingThrottle), EnumData.GetEnumValue(ThrottleRev.DirectionAndMagnitude) ) )); var result = rule.GetRule(); result.Conditions = new Condition[] { condition }; return(result); }
public override void Decompile() { if (Rule.EventInfo.Event != RuleEvent.Subroutine) { if (Rule.Disabled) { Decompiler.Append("disabled "); } Decompiler.Append("rule: \"" + Rule.Name + "\""); if (Rule.EventInfo.Event != RuleEvent.OngoingGlobal) { Decompiler.NewLine(); Decompiler.Append("Event." + EnumData.GetEnumValue(Rule.EventInfo.Event).CodeName); // Write the event. if (Rule.EventInfo.Team != Team.All) { Decompiler.NewLine(); Decompiler.Append("Team." + EnumData.GetEnumValue(Rule.EventInfo.Team).CodeName); } // Write the player. if (Rule.EventInfo.Player != PlayerSelector.All) { Decompiler.NewLine(); Decompiler.Append("Player." + EnumData.GetEnumValue(Rule.EventInfo.Player).CodeName); } } // Decompile conditions foreach (var condition in Rule.Conditions) { condition.Decompile(this); } } else { Decompiler.Append("void " + Rule.EventInfo.SubroutineName + "() \"" + Rule.Name + "\""); } Decompiler.AddBlock(); DecompileActions(); Decompiler.Outdent(); Decompiler.Append("}"); Decompiler.NewLine(); Decompiler.NewLine(); }
/// <summary>Throttles the event player to the next node.</summary> public void ThrottleEventPlayerToNextNode(ActionSet actionSet) { // Start throttle to the current node. actionSet.AddAction(Element.Part <A_StartThrottleInDirection>( Element.Part <V_EventPlayer>(), Element.Part <V_DirectionTowards>( Element.Part <V_PositionOf>(Element.Part <V_EventPlayer>()), // Go to the destination once the final node is reached. CurrentPositionWithDestination() ), new V_Number(1), // Magnitude EnumData.GetEnumValue(Relative.ToWorld), // Relative EnumData.GetEnumValue(ThrottleBehavior.ReplaceExistingThrottle), // Throttle Behavior EnumData.GetEnumValue(ThrottleRev.DirectionAndMagnitude) // Throttle Reevaluation )); }
public override object VisitEnum(DeltinScriptParser.EnumContext context) { string type = context.PART(0).GetText(); string value = context.PART(1)?.GetText(); if (value == null) { _diagnostics.Error("Expected enum value.", new Location(_file, Range.GetRange(context))); } else if (EnumData.GetEnumValue(type, value) == null) { _diagnostics.Error(string.Format(SyntaxErrorException.invalidEnumValue, value, type), new Location(_file, Range.GetRange(context))); } return(base.VisitEnum(context)); }
void ExecuteSubroutine(Subroutine subroutine, CallParallel executeOption = CallParallel.NoParallel) { switch (executeOption) { case CallParallel.NoParallel: ActionSet.AddAction(Element.Part <A_CallSubroutine>(subroutine)); break; case CallParallel.AlreadyRunning_DoNothing: ActionSet.AddAction(Element.Part <A_StartRule>(subroutine, EnumData.GetEnumValue(IfAlreadyExecuting.DoNothing))); break; case CallParallel.AlreadyRunning_RestartRule: ActionSet.AddAction(Element.Part <A_StartRule>(subroutine, EnumData.GetEnumValue(IfAlreadyExecuting.RestartRule))); break; } }
public static FuncMethod WorkshopSettingHero(DeltinScript deltinScript) => new FuncMethodBuilder() { Name = "WorkshopSettingHero", Documentation = "Provides the value of a new hero setting that will appear in the Workshop Settings card as a combo box.", Parameters = new CodeParameter[] { new ConstStringParameter("category", "The name of the category in which this setting can be found."), new ConstStringParameter("name", "The name of this setting."), new ConstHeroParameter("default", "The default value for this setting."), new ConstNumberParameter("sortOrder", "The sort order of this setting relative to other settings in the same category. Settings with a higher sort order will come after settings with a lower sort order.") }, DoesReturnValue = true, Action = (actionSet, methodCall) => Element.Part <V_WorkshopSettingHero>( new V_CustomString((string)methodCall.AdditionalParameterData[0]), new V_CustomString((string)methodCall.AdditionalParameterData[1]), EnumData.GetEnumValue(((ConstHeroValueResolver)methodCall.AdditionalParameterData[2]).Hero), new V_Number((double)methodCall.AdditionalParameterData[3]) ) };
void ParseConditions(ScopeGroup scope, Node[] expressions) { foreach (var expr in expressions) { Element parsedIf = ParseExpression(scope, scope, 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, expr.Location); } Element right = (Element)parsedIf.ParameterValues[2]; if (!right.ElementData.IsValue) { throw SyntaxErrorException.InvalidMethodType(true, right.Name, expr.Location); } 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, expr.Location); } Conditions.Add(new Condition( parsedIf, EnumData.GetEnumValue(Operators.Equal), new V_True() )); } } }
protected Element CreateLine(Line line, Element visibleTo, Element location, Element scale, IWorkshopTree reevaluation) { Element pos1 = line.Vertex1.ToVector(); Element pos2 = line.Vertex2.ToVector(); if (scale != null) { pos1 = Element.Part <V_Multiply>(pos1, scale); pos2 = Element.Part <V_Multiply>(pos2, scale); } return(Element.Part <A_CreateBeamEffect>( visibleTo, EnumData.GetEnumValue(BeamType.GrappleBeam), Element.Part <V_Add>(location, pos1), Element.Part <V_Add>(location, pos2), EnumData.GetEnumValue(Elements.Color.Red), reevaluation )); }
public override IWorkshopTree Get(ActionSet actionSet, IWorkshopTree[] parameterValues, object[] additionalParameterData) { Element player = (Element)parameterValues[0]; IndexReference originalHero = actionSet.VarCollection.Assign("isAI_originalHero", actionSet.IsGlobal, true); IndexReference isAI = actionSet.VarCollection.Assign("isAI_originalHero", actionSet.IsGlobal, true); actionSet.AddAction(ArrayBuilder <Element> .Build ( originalHero.SetVariable(Element.Part <V_HeroOf>(player)), Element.Part <A_SkipIf>(Element.Part <V_Compare>(originalHero.GetVariable(), EnumData.GetEnumValue(Operators.NotEqual), new V_Null()), new V_Number(2)), isAI.SetVariable(new V_False()), Element.Part <A_Skip>(new V_Number(4)), Element.Part <A_ForcePlayerHero>(player, EnumData.GetEnumValue(Hero.Ashe)), isAI.SetVariable(Element.Part <V_Compare>(Element.Part <V_HeroOf>(player), EnumData.GetEnumValue(Operators.NotEqual), EnumData.GetEnumValue(Hero.Ashe))), Element.Part <A_ForcePlayerHero>(player, originalHero.GetVariable()), Element.Part <A_StopForcingHero>(player) )); return(isAI.GetVariable()); }
public void Setup() { if (IsSetup) { return; } IsSetup = true; SkipCount = VarCollection.AssignVar(null, "ContinueSkip", IsGlobal, null); TempHolder = VarCollection.AssignVar(null, "ContinueSkip temp holder", IsGlobal, null); A_Wait waitAction = A_Wait.MinimumWait; waitAction.Comment = "ContinueSkip Wait"; A_SkipIf skipAction = Element.Part <A_SkipIf> ( // Condition Element.Part <V_Compare>(SkipCount.GetVariable(), EnumData.GetEnumValue(Operators.Equal), new V_Number(0)), // Number of actions new V_Number(3) ); skipAction.Comment = "ContinueSkip Skipper"; Element[] actions = ArrayBuilder <Element> .Build( waitAction, skipAction, TempHolder.SetVariable(SkipCount.GetVariable()), SkipCount.SetVariable(new V_Number(0)), Element.Part <A_Skip>(TempHolder.GetVariable()) ); if (actions.Length != ExpectedActionCount) { throw new Exception($"Expected {ExpectedActionCount} actions for the Continue Skip, got {actions.Length} instead."); } Actions.InsertRange(0, actions); }
// Enumerator Values bool EnumeratorValue(out ITTEExpression expr) { if (Match(Kw("All Teams"))) { expr = new ConstantEnumeratorExpression(EnumData.GetEnumValue(Team.All)); return(true); } if (Match(Kw("Team 1"))) { expr = new ConstantEnumeratorExpression(EnumData.GetEnumValue(Team.Team1)); return(true); } if (Match(Kw("Team 2"))) { expr = new ConstantEnumeratorExpression(EnumData.GetEnumValue(Team.Team2)); return(true); } // TODO: Gamemode, map, button, etc expr = null; return(false); }
public void Setup() { if (IsSetup) { return; } IsSetup = true; SkipCount = VarCollection.AssignVar("ContinueSkip", IsGlobal); // Add the required wait Actions.Insert(0, Element.Part <A_Wait>(new V_Number(Constants.MINIMUM_WAIT))); // Add the skip-if Actions.Insert(1, Element.Part <A_SkipIf> ( Element.Part <V_Compare>(SkipCount.GetVariable(), EnumData.GetEnumValue(Operators.NotEqual), new V_Number(0)), SkipCount.GetVariable() ) ); }
public override object VisitEnum(DeltinScriptParser.EnumContext context) { string type = context.ENUM().GetText(); string value = context.PART()?.GetText(); if (value == null) { _diagnostics.Add(new Diagnostic("Expected enum value.", Range.GetRange(context)) { severity = Diagnostic.Error }); } else if (EnumData.GetEnumValue(type, value) == null) { _diagnostics.Add(new Diagnostic(string.Format(SyntaxErrorException.invalidEnumValue, value, type), Range.GetRange(context)) { severity = Diagnostic.Error }); } return(base.VisitEnum(context)); }
private static IWorkshopTree CallSubroutine(DefinedMethod method, MethodCall call, ActionSet callerSet) { method.SetupSubroutine(); for (int i = 0; i < method.subroutineInfo.ParameterStores.Length; i++) { // Normal parameter push. if (!method.Attributes.Recursive) { callerSet.AddAction(method.subroutineInfo.ParameterStores[i].SetVariable((Element)call.ParameterValues[i])); } // Recursive parameter push. else { callerSet.AddAction(((RecursiveIndexReference)method.subroutineInfo.ParameterStores[i]).Push((Element)call.ParameterValues[i])); } } // Store the object the subroutine is executing with. if (method.subroutineInfo.ObjectStore != null) { // Normal if (!method.Attributes.Recursive) { callerSet.AddAction(method.subroutineInfo.ObjectStore.SetVariable((Element)callerSet.CurrentObject)); } // Recursive: Stack else { callerSet.AddAction(method.subroutineInfo.ObjectStore.ModifyVariable(Operation.AppendToArray, (Element)callerSet.CurrentObject)); } } switch (call.CallParallel) { // No parallel, call subroutine normally. case CallParallel.NoParallel: callerSet.AddAction(Element.Part <A_CallSubroutine>(method.subroutineInfo.Subroutine)); return(method.subroutineInfo.ReturnHandler.GetReturnedValue()); // Restart the subroutine if it is already running. case CallParallel.AlreadyRunning_RestartRule: callerSet.AddAction(Element.Part <A_StartRule>(method.subroutineInfo.Subroutine, EnumData.GetEnumValue(IfAlreadyExecuting.RestartRule))); return(null); // Do nothing if the subroutine is already running. case CallParallel.AlreadyRunning_DoNothing: callerSet.AddAction(Element.Part <A_StartRule>(method.subroutineInfo.Subroutine, EnumData.GetEnumValue(IfAlreadyExecuting.DoNothing))); return(null); default: throw new NotImplementedException(); } }
private void ParseVirtual() { // Loop through all potential methods. DefinedMethod[] options = Array.ConvertAll(Method.Attributes.AllOverrideOptions(), iMethod => (DefinedMethod)iMethod); // Create the switch that chooses the overload. SwitchBuilder typeSwitch = new SwitchBuilder(BuilderSet); // Parse the current overload. if (Method.Attributes.Virtual) { typeSwitch.AddDefault(); } else { typeSwitch.NextCase(new V_Number(((ClassType)Method.Attributes.ContainingType).Identifier)); } TranslateSegment(BuilderSet, Method); foreach (DefinedMethod option in options) { // The action set for the overload. ActionSet optionSet = BuilderSet.New(BuilderSet.IndexAssigner.CreateContained()); // Add the object variables of the selected method. option.Attributes.ContainingType.AddObjectVariablesToAssigner(optionSet.CurrentObject, optionSet.IndexAssigner); // Go to next case then parse the block. typeSwitch.NextCase(new V_Number(((ClassType)option.Attributes.ContainingType).Identifier)); // Iterate through every type. foreach (CodeType type in BuilderSet.Translate.DeltinScript.Types.AllTypes) { // If 'type' does not equal the current virtual option's containing class... if (option.Attributes.ContainingType != type // ...and 'type' implements the containing class... && type.Implements(option.Attributes.ContainingType) // ...and 'type' does not have their own function implementation... && AutoImplemented(option.Attributes.ContainingType, options.Select(o => o.Attributes.ContainingType).ToArray(), type)) { // ...then add an additional case for 'type's class identifier. typeSwitch.NextCase(new V_Number(((ClassType)type).Identifier)); } } if (option.subroutineInfo == null) { TranslateSegment(optionSet, option); } else { option.SetupSubroutine(); BuilderSet.AddAction(Element.Part <A_StartRule>(option.subroutineInfo.Subroutine, EnumData.GetEnumValue(IfAlreadyExecuting.DoNothing))); if (Method.DoesReturnValue) { ReturnHandler.ReturnValue(option.subroutineInfo.ReturnHandler.GetReturnedValue()); } } if (Method.IsSubroutine) { option.virtualSubroutineAssigned = Method; } } ClassData classData = BuilderSet.Translate.DeltinScript.GetComponent <ClassData>(); // Finish the switch. typeSwitch.Finish(Element.Part <V_ValueInArray>(classData.ClassIndexes.GetVariable(), BuilderSet.CurrentObject)); }
public EnumNode(string type, string value, Range range) : base(range) { Type = type; Value = value; EnumMember = EnumData.GetEnumValue(type, value); }
override public Element Get(TranslateRule context, ScopeGroup scope, MethodNode methodNode, IWorkshopTree[] parameters) { Element result; if (!IsRecursive) { // Check the method stack if this method was already called. // Throw a syntax error if it was. if (context.MethodStackNotRecursive.Contains(this)) { throw SyntaxErrorException.RecursionNotAllowed(methodNode.Location); } var methodScope = scope.Root().Child(); // Add the parameter variables to the scope. context.AssignParameterVariables(methodScope, Parameters, parameters, methodNode); // The variable that stores the return value. IndexedVar returns = null; if (DoesReturn) { returns = IndexedVar.AssignVar(context.VarCollection, scope, $"{methodNode.Name} return", context.IsGlobal, null); returns.Type = Type; } // Add the method to the method stack context.MethodStackNotRecursive.Add(this); Block.RelatedScopeGroup = methodScope; // Parse the block of the method context.ParseBlock(methodScope, methodScope, Block, true, returns); // Take the method scope out of scope. methodScope.Out(context); // Remove the method from the stack. context.MethodStackNotRecursive.Remove(this); if (DoesReturn) { result = returns.GetVariable(); } else { result = new V_Null(); } } else { // Check the method stack if this method was already called. It will be null if it wasn't called. MethodStack lastMethod = context.MethodStackRecursive.FirstOrDefault(ms => ms.UserMethod == this); if (lastMethod != null) { context.ContinueSkip.Setup(); // Re-push the paramaters. for (int i = 0; i < lastMethod.ParameterVars.Length; i++) { if (lastMethod.ParameterVars[i] is RecursiveVar) { context.Actions.AddRange ( ((RecursiveVar)lastMethod.ParameterVars[i]).InScope((Element)parameters[i]) ); } } // Add to the continue skip array. context.Actions.AddRange( lastMethod.ContinueSkipArray.SetVariable( Element.Part <V_Append>(lastMethod.ContinueSkipArray.GetVariable(), new V_Number(context.ContinueSkip.GetSkipCount() + 3)) ) ); // Loop back to the start of the method. context.ContinueSkip.SetSkipCount(lastMethod.ActionIndex); context.Actions.Add(Element.Part <A_Loop>()); result = lastMethod.Return.GetVariable(); } else { var methodScope = scope.Root().Child(true); // Add the parameter variables to the scope. Var[] parameterVars = new Var[Parameters.Length]; for (int i = 0; i < parameterVars.Length; i++) { if (parameters[i] is Element) { // Create a new variable using the parameter input. parameterVars[i] = (RecursiveVar)IndexedVar.AssignVar(context.VarCollection, methodScope, Parameters[i].Name, context.IsGlobal, methodNode); ((RecursiveVar)parameterVars[i]).Type = ((Element)parameters[i]).SupportedType?.Type; context.Actions.AddRange ( ((RecursiveVar)parameterVars[i]).InScope((Element)parameters[i]) ); } else if (parameters[i] is EnumMember) { parameterVars[i] = new ElementReferenceVar(Parameters[i].Name, methodScope, methodNode, parameters[i]); } else { throw new NotImplementedException(); } } var returns = IndexedVar.AssignInternalVarExt(context.VarCollection, null, $"{methodNode.Name}: return", context.IsGlobal); returns.Type = Type; // Setup the continue skip array. IndexedVar continueSkipArray = IndexedVar.AssignInternalVar(context.VarCollection, null, $"{methodNode.Name} sca", context.IsGlobal); var stack = new MethodStack(this, parameterVars, context.ContinueSkip.GetSkipCount(), returns, continueSkipArray); // Add the method to the stack. context.MethodStackRecursive.Add(stack); Block.RelatedScopeGroup = methodScope; // Parse the method block context.ParseBlock(methodScope, methodScope, Block, true, returns); // No return value if the method is being used as an action. result = returns.GetVariable(); // Take the method out of scope. //Actions.AddRange(methodScope.RecursiveMethodStackPop()); methodScope.Out(context); // Setup the next continue skip. context.ContinueSkip.Setup(); context.ContinueSkip.SetSkipCount(Element.Part <V_LastOf>(continueSkipArray.GetVariable())); // Remove the last continue skip. context.Actions.AddRange( continueSkipArray.SetVariable( Element.Part <V_ArraySlice>( continueSkipArray.GetVariable(), new V_Number(0), Element.Part <V_CountOf>(continueSkipArray.GetVariable()) - 1 ) ) ); // Loop if the method goes any deeper by checking the length of the continue skip array. context.Actions.Add( Element.Part <A_LoopIf>( Element.Part <V_Compare>( Element.Part <V_CountOf>(continueSkipArray.GetVariable()), EnumData.GetEnumValue(Operators.NotEqual), new V_Number(0) ) ) ); // Reset the continue skip. context.ContinueSkip.ResetSkip(); context.Actions.AddRange(continueSkipArray.SetVariable(0)); // Remove the method from the stack. context.MethodStackRecursive.Remove(stack); } } return(result); }
public static Element Node2(Element segment) => Element.Part <V_RoundToInteger>(Element.Part <V_YOf>(segment), EnumData.GetEnumValue(Rounding.Down));
public static Element Node2Attribute(Element segment) => Element.Part <V_RoundToInteger>( (Element.Part <V_YOf>(segment) % 1) * 100, EnumData.GetEnumValue(Rounding.Nearest) );
protected void CreateLine(ActionSet actionSet, Line line, Element visibleTo, Element location, Element rotation, Element scale, IWorkshopTree reevaluation) { Vertex vertex1 = line.Vertex1; Vertex vertex2 = line.Vertex2; Element pos1; Element pos2; bool scaleSet = false; bool rotationSet = false; if (scale != null) { double?constantScale = null; // Double constant scale if (scale.ConstantSupported <double>()) { constantScale = (double)scale.GetConstant(); } // Null constant rotation else if (scale is V_Null) { constantScale = 1; } if (constantScale == 1) { scaleSet = true; } if (!scaleSet && constantScale != null) { vertex1 = vertex1.Scale((double)constantScale); vertex2 = vertex2.Scale((double)constantScale); scaleSet = true; } } if (rotation != null) { Vertex rotationConstant = null; // Vector constant rotation if (rotation.ConstantSupported <Vertex>()) { rotationConstant = (Vertex)rotation.GetConstant(); } // Double constant rotation else if (rotation.ConstantSupported <double>()) { rotationConstant = new Vertex(0, (double)rotation.GetConstant(), 0); } // Null constant rotation else if (rotation is V_Null) { rotationConstant = new Vertex(0, 0, 0); } if (rotationConstant != null && rotationConstant.EqualTo(new Vertex(0, 0, 0))) { rotationSet = true; } if (rotationConstant != null && !rotationSet) { vertex1 = vertex1.Rotate(rotationConstant); vertex2 = vertex2.Rotate(rotationConstant); rotationSet = true; } } if (rotation != null && !rotationSet) { var pos1X = vertex1.X; var pos1Y = vertex1.Y; var pos1Z = vertex1.Z; var pos2X = vertex2.X; var pos2Y = vertex2.Y; var pos2Z = vertex2.Z; var yaw = Element.Part <V_HorizontalAngleFromDirection>(rotation); var pitch = Element.Part <V_VerticalAngleFromDirection>(rotation); var cosa = Element.Part <V_CosineFromDegrees>(pitch); var sina = Element.Part <V_SineFromDegrees>(pitch); var cosb = Element.Part <V_CosineFromDegrees>(yaw); var sinb = Element.Part <V_SineFromDegrees>(yaw); var Axx = cosa * cosb; var Axy = 0 - sina; var Axz = cosa * sinb; var Ayx = sina * cosb; var Ayy = cosa; var Ayz = sina * sinb; var Azx = -sinb; pos1 = Element.Part <V_Vector>( Axx * pos1X + Axy * pos1Y + Axz * pos1Z, Ayx * pos1X + Ayy * pos1Y + Ayz * pos1Z, Azx * pos1X + pos1Z ); pos2 = Element.Part <V_Vector>( Axx * pos2X + Axy * pos2Y + Axz * pos2Z, Ayx * pos2X + Ayy * pos2Y + Ayz * pos2Z, Azx * pos2X + pos2Z ); } else { pos1 = vertex1.ToVector(); pos2 = vertex2.ToVector(); } if (scale != null && !scaleSet) { pos1 = pos1 * scale; pos2 = pos2 * scale; } actionSet.AddAction(Element.Part <A_CreateBeamEffect>( visibleTo, EnumData.GetEnumValue(BeamType.GrappleBeam), location + pos1, location + pos2, EnumData.GetEnumValue(Elements.Color.Red), reevaluation )); }
// Calls single-instance methods. private IWorkshopTree ParseSubroutine(ActionSet actionSet, MethodCall methodCall) { if (subroutineInfo == null) { SetupSubroutine(); } for (int i = 0; i < subroutineInfo.ParameterStores.Length; i++) { actionSet.AddAction(subroutineInfo.ParameterStores[i].SetVariable((Element)methodCall.ParameterValues[i])); } if (subroutineInfo.ObjectStore != null) { actionSet.AddAction(subroutineInfo.ObjectStore.SetVariable(actionSet.CurrentObject)); } switch (methodCall.CallParallel) { // No parallel, call subroutine normally. case CallParallel.NoParallel: actionSet.AddAction(Element.Part <A_CallSubroutine>(subroutineInfo.Subroutine)); return(subroutineInfo.ReturnHandler.GetReturnedValue()); // Restart the subroutine if it is already running. case CallParallel.AlreadyRunning_RestartRule: actionSet.AddAction(Element.Part <A_StartRule>(subroutineInfo.Subroutine, EnumData.GetEnumValue(IfAlreadyExecuting.RestartRule))); return(null); // Do nothing if the subroutine is already running. case CallParallel.AlreadyRunning_DoNothing: actionSet.AddAction(Element.Part <A_StartRule>(subroutineInfo.Subroutine, EnumData.GetEnumValue(IfAlreadyExecuting.DoNothing))); return(null); default: throw new NotImplementedException(); } }
public EnumNode(string type, string value, Location location) : base(location) { Type = type; Value = value; EnumMember = EnumData.GetEnumValue(type, value); }
public override CodeParameter[] Parameters() => new CodeParameter[] { new VariableParameter("variable", "The variable to manipulate. Player variables will chase the event player's variable. Must be a variable defined on the rule level.", VariableType.Dynamic, new VariableResolveOptions() { CanBeIndexed = false, FullVariable = true }), new CodeParameter("destination", "The value that the variable will eventually reach. The type of this value may be either a number or a vector, through the variable’s existing value must be of the same type before the chase begins. Can use number or vector based values."), new CodeParameter("rate", "The amount of change that will happen to the variable’s value each second."), new CodeParameter("reevaluation", "Specifies which of this action's inputs will be continuously reevaluated. This action will keep asking for and using new values from reevaluated inputs.", ValueGroupType.GetEnumType <RateChaseReevaluation>(), new ExpressionOrWorkshopValue(EnumData.GetEnumValue(RateChaseReevaluation.DestinationAndRate))) };
public Element GetArray() { return(Element.CreateArray(Maps.Select(m => EnumData.GetEnumValue(m)).ToArray())); }
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); }
void ParseStatement(ScopeGroup scope, IStatementNode statement, Var returnVar, bool isLast) { switch (statement) { // Method case MethodNode methodNode: Element method = ParseMethod(scope, methodNode, false); if (method != null) { Actions.Add(method); } return; // Variable set case VarSetNode varSetNode: ParseVarset(scope, varSetNode); return; // For case ForEachNode forEachNode: { ContinueSkip.Setup(); // The action the for loop starts on. int forActionStartIndex = Actions.Count() - 1; ScopeGroup forGroup = scope.Child(); // Create the for's temporary variable. DefinedVar forTempVar = VarCollection.AssignDefinedVar( scopeGroup: forGroup, name: forEachNode.Variable, isGlobal: IsGlobal, range: forEachNode.Range ); // Reset the counter. Actions.Add(forTempVar.SetVariable(new V_Number(0))); // Parse the for's block. ParseBlock(forGroup, forEachNode.Block, false, returnVar); // Add the for's finishing elements Actions.Add(forTempVar.SetVariable( // Indent the index by 1. Element.Part <V_Add> ( forTempVar.GetVariable(), new V_Number(1) ) )); ContinueSkip.SetSkipCount(forActionStartIndex); // The target array in the for statement. Element forArrayElement = ParseExpression(scope, forEachNode.Array); Actions.Add(Element.Part <A_LoopIf>( // Loop if the for condition is still true. Element.Part <V_Compare> ( forTempVar.GetVariable(), EnumData.GetEnumValue(Operators.LessThan), Element.Part <V_CountOf>(forArrayElement) ) )); ContinueSkip.ResetSkip(); return; } // For case ForNode forNode: { ContinueSkip.Setup(); // The action the for loop starts on. int forActionStartIndex = Actions.Count() - 1; ScopeGroup forGroup = scope.Child(); // Set the variable if (forNode.VarSetNode != null) { ParseVarset(scope, forNode.VarSetNode); } if (forNode.DefineNode != null) { ParseDefine(scope, forNode.DefineNode); } // Parse the for's block. ParseBlock(forGroup, forNode.Block, false, returnVar); Element expression = null; if (forNode.Expression != null) { expression = ParseExpression(forGroup, forNode.Expression); } // Check the expression if (forNode.Expression != null) // If it has an expression { // Parse the statement if (forNode.Statement != null) { ParseStatement(forGroup, forNode.Statement, returnVar, false); } ContinueSkip.SetSkipCount(forActionStartIndex); Actions.Add(Element.Part <A_LoopIf>(expression)); } // If there is no expression but there is a statement, parse the statement. else if (forNode.Statement != null) { ParseStatement(forGroup, forNode.Statement, returnVar, false); ContinueSkip.SetSkipCount(forActionStartIndex); // Add the loop Actions.Add(Element.Part <A_Loop>()); } ContinueSkip.ResetSkip(); return; } // While case WhileNode whileNode: { ContinueSkip.Setup(); // The action the while loop starts on. int whileStartIndex = Actions.Count() - 2; ScopeGroup whileGroup = scope.Child(); ParseBlock(whileGroup, whileNode.Block, false, returnVar); ContinueSkip.SetSkipCount(whileStartIndex); // Add the loop-if Element expression = ParseExpression(scope, whileNode.Expression); Actions.Add(Element.Part <A_LoopIf>(expression)); ContinueSkip.ResetSkip(); return; } // If case IfNode ifNode: { A_SkipIf if_SkipIf = new A_SkipIf(); Actions.Add(if_SkipIf); var ifScope = scope.Child(); // Parse the if body. ParseBlock(ifScope, ifNode.IfData.Block, false, returnVar); // Determines if the "Skip" action after the if block will be created. // Only if there is if-else or else statements. bool addIfSkip = ifNode.ElseIfData.Length > 0 || ifNode.ElseBlock != null; // Update the initial SkipIf's skip count now that we know the number of actions the if block has. // Add one to the body length if a Skip action is going to be added. if_SkipIf.ParameterValues = new IWorkshopTree[] { Element.Part <V_Not>(ParseExpression(scope, ifNode.IfData.Expression)), new V_Number(Actions.Count - 1 - Actions.IndexOf(if_SkipIf) + (addIfSkip ? 1 : 0)) }; // Create the "Skip" action. A_Skip if_Skip = new A_Skip(); if (addIfSkip) { Actions.Add(if_Skip); } // Parse else-ifs A_Skip[] elseif_Skips = new A_Skip[ifNode.ElseIfData.Length]; // The ElseIf's skips for (int i = 0; i < ifNode.ElseIfData.Length; i++) { // Create the SkipIf action for the else if. A_SkipIf elseif_SkipIf = new A_SkipIf(); Actions.Add(elseif_SkipIf); // Parse the else-if body. var elseifScope = scope.Child(); ParseBlock(elseifScope, ifNode.ElseIfData[i].Block, false, returnVar); // Determines if the "Skip" action after the else-if block will be created. // Only if there is additional if-else or else statements. bool addIfElseSkip = i < ifNode.ElseIfData.Length - 1 || ifNode.ElseBlock != null; // Set the SkipIf's parameters. elseif_SkipIf.ParameterValues = new IWorkshopTree[] { Element.Part <V_Not>(ParseExpression(scope, ifNode.ElseIfData[i].Expression)), new V_Number(Actions.Count - 1 - Actions.IndexOf(elseif_SkipIf) + (addIfElseSkip ? 1 : 0)) }; // Create the "Skip" action for the else-if. if (addIfElseSkip) { elseif_Skips[i] = new A_Skip(); Actions.Add(elseif_Skips[i]); } } // Parse else body. if (ifNode.ElseBlock != null) { var elseScope = scope.Child(); ParseBlock(elseScope, ifNode.ElseBlock, false, returnVar); } // Replace dummy skip with real skip now that we know the length of the if, if-else, and else's bodies. // Replace if's dummy. if_Skip.ParameterValues = new IWorkshopTree[] { new V_Number(Actions.Count - 1 - Actions.IndexOf(if_Skip)) }; // Replace else-if's dummy. for (int i = 0; i < elseif_Skips.Length; i++) { if (elseif_Skips[i] != null) { elseif_Skips[i].ParameterValues = new IWorkshopTree[] { new V_Number(Actions.Count - 1 - Actions.IndexOf(elseif_Skips[i])) }; } } return; } // Return case ReturnNode returnNode: if (returnNode.Value != null) { Element result = ParseExpression(scope, returnNode.Value); if (returnVar != null) { Actions.Add(returnVar.SetVariable(result)); } } if (!isLast) { A_Skip returnSkip = new A_Skip(); Actions.Add(returnSkip); ReturnSkips.Add(returnSkip); } return; // Define case ScopedDefineNode defineNode: ParseDefine(scope, defineNode); return; } }