public InjectionValue CallSubrutine(injectionParser.SubrutineContext subrutine, InjectionValue[] argumentValues) { var forScopes = new Stack <ForScope>(); var repeatIndexes = new Stack <int>(); semanticScope.Start(); var parameters = subrutine.parameters()?.parameterName()?.Select(x => x.SYMBOL().GetText()).ToArray() ?? Array.Empty <string>(); for (int i = 0; i < parameters.Length; i++) { semanticScope.DefineVar(parameters[i], argumentValues[i]); } semanticScope.DefineGlobalVariables(metadata.GlobalVariables, globalVar => VisitExpression(globalVar.InitialValueExpression)); var name = subrutine.subrutineName().GetText(); var subrutineDefinition = metadata.GetSubrutine(name, parameters.Length); try { var instructionAddress = 0; while (instructionAddress < subrutineDefinition.Instructions.Length) { var currentInstruction = subrutineDefinition.Instructions[instructionAddress]; var statement = currentInstruction.Statement; if (statement == null) { switch (currentInstruction) { case JumpInstruction jump: instructionAddress = jump.TargetAddress; break; } continue; } if (debugger != null) { var context = new StatementExecutionContext(instructionAddress, statement.Start.Line, currentFileName, statement, this); debugger.BeforeStatement(context); } try { if (statement.returnStatement() != null) { return(Visit(statement.returnStatement())); } else if (statement.@if() != null) { var condition = Visit(statement.@if().expression()); if (condition == InjectionValue.False) { var ifInstruction = (IfInstruction)currentInstruction; instructionAddress = ifInstruction.ElseAddress ?? ifInstruction.EndIfAddress; } else { instructionAddress++; } } else if (statement.@for() != null) { instructionAddress++; forScopes.Push(InterpretFor(instructionAddress, statement.@for(), semanticScope)); } else if (statement.next() != null) { var forScope = forScopes.Peek(); if (!semanticScope.TryGetVar(forScope.VariableName, out var variable)) { throw new ScriptFailedException($"Variable undefined - {forScope.VariableName}", subrutineDefinition.Instructions[instructionAddress].Statement.Start.Line); } if (variable < forScope.Range) { instructionAddress = forScope.StatementIndex; variable = variable + forScope.Step; semanticScope.SetVar(forScope.VariableName, variable); } else { instructionAddress++; forScopes.Pop(); } } else if (statement.repeat() != null) { instructionAddress++; repeatIndexes.Push(instructionAddress); } else if (statement.until() != null) { var condition = Visit(statement.until().expression()); if (condition != InjectionValue.False) { instructionAddress++; repeatIndexes.Pop(); } else { instructionAddress = repeatIndexes.Peek(); } } else if (statement.@while() != null) { var whileInstruction = (WhileInstruction)currentInstruction; var condition = Visit(statement.@while().expression()); if (condition != InjectionValue.False) { instructionAddress++; } else { instructionAddress = whileInstruction.WendAddress; } } else if (statement.@goto() != null) { var gotoInstruction = (GotoInstruction)currentInstruction; instructionAddress = gotoInstruction.TargetAddress; } else { Visit(statement); instructionAddress++; } } catch (StatementFailedException ex) { throw new ScriptFailedException(ex.Message, statement.Start.Line, ex); } catch (ScriptFailedException) { throw; } catch (OperationCanceledException) { throw; } catch (Exception ex) { throw new InternalInterpretationException(statement.Start.Line, ex); } } if (debugger != null) { debugger.BeforeStatement( new StatementExecutionContext(instructionAddress, subrutine.END_SUB().Symbol.Line, currentFileName, null, this)); } return(InjectionValue.Unit); } finally { semanticScope.End(); } }