예제 #1
0
        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();
            }
        }