예제 #1
0
        override public Element New(CreateObjectNode node, ScopeGroup getter, ScopeGroup scope, TranslateRule context)
        {
            IndexedVar store = IndexedVar.AssignInternalVarExt(context.VarCollection, scope, Name + " store", context.IsGlobal);

            store.Type = this;
            ScopeGroup typeScope = GetRootScope(null, store, context.ParserData);

            SetupNew(getter, scope, store, typeScope, context, node);

            return(store.GetVariable());
        }
        public void Setup()
        {
            if (IsSetup)
            {
                return;
            }
            IsSetup = true;

            SkipCount  = IndexedVar.AssignInternalVarExt(VarCollection, null, "ContinueSkip", IsGlobal);
            TempHolder = IndexedVar.AssignInternalVarExt(VarCollection, null, "ContinueSkip temp holder", IsGlobal);

            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(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);
        }
        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);
        }
예제 #4
0
        override public Element New(CreateObjectNode node, ScopeGroup getter, ScopeGroup scope, TranslateRule context)
        {
            context.ParserData.SetupClasses();

            // Get the index to store the class.
            IndexedVar index        = IndexedVar.AssignInternalVarExt(context.VarCollection, scope, "New " + Name + " class index", context.IsGlobal); // Assigns the index variable.
            Element    takenIndexes = context.ParserData.ClassIndexes.GetVariable();

            // Get an empty index in the class array to store the new class.
            Element firstFree = (
                Element.Part <V_FirstOf>(
                    Element.Part <V_FilteredArray>(
                        // Sort the taken index array.
                        Element.Part <V_SortedArray>(takenIndexes, new V_ArrayElement()),
                        // Filter
                        Element.Part <V_And>(
                            // If the previous index was not taken, use that index.
                            !(Element.Part <V_ArrayContains>(
                                  takenIndexes,
                                  new V_ArrayElement() - 1
                                  )),
                            // Make sure the index does not equal 0 so the resulting index is not -1.
                            new V_Compare(new V_ArrayElement(), Operators.NotEqual, new V_Number(0))
                            )
                        )
                    ) -
                1 // Subtract 1 to get the previous index
                );

            // If the taken index array has 0 elements, just use the length of the class array subtracted by 1.
            firstFree = Element.TernaryConditional(
                new V_Compare(Element.Part <V_CountOf>(takenIndexes), Operators.NotEqual, new V_Number(0)),
                firstFree,
                Element.Part <V_CountOf>(context.ParserData.ClassArray.GetVariable()) - 1
                );

            context.Actions.AddRange(index.SetVariable(firstFree));

            context.Actions.AddRange(
                index.SetVariable(
                    Element.TernaryConditional(
                        // If the index equals -1, use the length of the class array instead.
                        new V_Compare(index.GetVariable(), Operators.Equal, new V_Number(-1)),
                        Element.Part <V_CountOf>(context.ParserData.ClassArray.GetVariable()),
                        index.GetVariable()
                        )
                    )
                );

            // Add the selected index to the taken indexes array.
            context.Actions.AddRange(
                context.ParserData.ClassIndexes.SetVariable(
                    Element.Part <V_Append>(
                        context.ParserData.ClassIndexes.GetVariable(),
                        index.GetVariable()
                        )
                    )
                );

            // The direct reference to the class variable.
            IndexedVar store = context.ParserData.ClassArray.CreateChild(scope, Name + " root", new Element[] { index.GetVariable() }, null);

            store.Index[0].SupportedType = store;
            store.Type = this;

            ScopeGroup typeScope = GetRootScope(index.GetVariable(), store, context.ParserData);

            SetupNew(getter, scope, store, typeScope, context, node);

            return(index.GetVariable());
        }
        void ParseStatement(ScopeGroup getter, ScopeGroup scope, Node statement, IndexedVar returnVar)
        {
            switch (statement)
            {
            // Method
            case MethodNode methodNode:
                Element method = ParseMethod(getter, scope, methodNode, false);
                return;

            // Variable set
            case VarSetNode varSetNode:
                ParseVarset(getter, scope, varSetNode);
                return;

            // Foreach
            case ForEachNode forEachNode:
            {
                ContinueSkip.Setup();

                ScopeGroup forGroup = scope.Child();

                Element array = ParseExpression(getter, scope, forEachNode.Array);

                IndexedVar index = IndexedVar.AssignInternalVarExt(VarCollection, scope, $"'{forEachNode.Variable.VariableName}' for index", IsGlobal);

                int offset = 0;

                Element getVariableReference()
                {
                    return(Element.Part <V_ValueInArray>(array, indexer()));
                }

                Element indexer()
                {
                    if (offset == 0)
                    {
                        return(index.GetVariable());
                    }
                    else
                    {
                        return(index.GetVariable() + getOffset());
                    }
                }

                V_Number getOffset()
                {
                    return(new V_Number(offset));
                }

                IndexedVar    arrayVar = null;
                ElementOrigin origin   = ElementOrigin.GetElementOrigin(array);
                if (origin == null && forEachNode.Variable.Type != null)
                {
                    throw new SyntaxErrorException("Could not get the type source.", forEachNode.Variable.Location);
                }
                if (origin != null)
                {
                    arrayVar = origin.OriginVar(VarCollection, null, null);
                }

                // Reset the counter.
                Actions.AddRange(index.SetVariable(0));

                // The action the for loop starts on.
                int forStartIndex = ContinueSkip.GetSkipCount();

                A_SkipIf skipCondition = new A_SkipIf()
                {
                    ParameterValues = new IWorkshopTree[2]
                };
                skipCondition.ParameterValues[0] =
                    !(index.GetVariable() < Element.Part <V_CountOf>(array)
                      );
                Actions.Add(skipCondition);

                List <A_SkipIf> rangeSkips = new List <A_SkipIf>();
                for (; offset < forEachNode.Repeaters; offset++)
                {
                    if (offset > 0)
                    {
                        //variable.Reference = getVariableReference();

                        A_SkipIf skipper = new A_SkipIf()
                        {
                            ParameterValues = new Element[2]
                        };
                        skipper.ParameterValues[0] = !(
                            index.GetVariable() + getOffset() < Element.Part <V_CountOf>(array)
                            );
                        rangeSkips.Add(skipper);
                        Actions.Add(skipper);
                    }

                    // Parse the for's block. Use a child to prevent conflicts with repeaters.
                    ScopeGroup tempChild = forGroup.Child();

                    Var variable;
                    if (arrayVar != null)
                    {
                        variable = arrayVar.CreateChild(tempChild, forEachNode.Variable.VariableName, new Element[] { indexer() }, forEachNode.Variable);
                        if (forEachNode.Variable.Type != null)
                        {
                            variable.Type = ParserData.GetDefinedType(forEachNode.Variable.Type, forEachNode.Variable.Location);
                        }
                    }
                    else
                    {
                        variable = new ElementReferenceVar(forEachNode.Variable.VariableName, tempChild, forEachNode, getVariableReference());
                    }

                    ParseBlock(tempChild, tempChild, forEachNode.Block, false, returnVar);
                    tempChild.Out(this);
                }
                // Take the foreach out of scope.
                forGroup.Out(this);

                // Increment the index
                Actions.AddRange(index.SetVariable(index.GetVariable() + forEachNode.Repeaters));

                // Add the for's finishing elements
                ContinueSkip.SetSkipCount(forStartIndex);
                Actions.Add(Element.Part <A_Loop>());

                rangeSkips.ForEach(var => var.ParameterValues[1] = new V_Number(GetSkipCount(var)));

                // Set the skip
                if (skipCondition != null)
                {
                    skipCondition.ParameterValues[1] = new V_Number(GetSkipCount(skipCondition));
                }

                ContinueSkip.ResetSkip();
                return;
            }

            // For
            case ForNode forNode:
            {
                ContinueSkip.Setup();

                ScopeGroup forContainer = scope.Child();

                // Set the variable
                if (forNode.VarSetNode != null)
                {
                    ParseVarset(getter, scope, forNode.VarSetNode);
                }
                if (forNode.DefineNode != null)
                {
                    ParseDefine(getter, forContainer, forNode.DefineNode);
                }

                ScopeGroup forGroup = forContainer.Child();

                // The action the for loop starts on.
                int forStartIndex = ContinueSkip.GetSkipCount();

                A_SkipIf skipCondition = null;
                // Skip if the condition is false.
                if (forNode.Expression != null)     // If it has an expression
                {
                    skipCondition = new A_SkipIf()
                    {
                        ParameterValues = new IWorkshopTree[2]
                    };
                    skipCondition.ParameterValues[0] = !(ParseExpression(forGroup, forGroup, forNode.Expression));
                    Actions.Add(skipCondition);
                }

                // Parse the for's block.
                ParseBlock(forGroup, forGroup, forNode.Block, false, returnVar);

                // Parse the statement
                if (forNode.Statement != null)
                {
                    ParseVarset(forGroup, forGroup, forNode.Statement);
                }

                // Take the for out of scope.
                forGroup.Out(this);

                ContinueSkip.SetSkipCount(forStartIndex);
                Actions.Add(Element.Part <A_Loop>());

                // Set the skip
                if (skipCondition != null)
                {
                    skipCondition.ParameterValues[1] = new V_Number(GetSkipCount(skipCondition));
                }

                // Take the defined variable in the for out of scope
                forContainer.Out(this);

                ContinueSkip.ResetSkip();
                return;
            }

            // While
            case WhileNode whileNode:
            {
                ContinueSkip.Setup();

                // The action the while loop starts on.
                int whileStartIndex = ContinueSkip.GetSkipCount();

                A_SkipIf skipCondition = new A_SkipIf()
                {
                    ParameterValues = new IWorkshopTree[2]
                };
                skipCondition.ParameterValues[0] = !(ParseExpression(getter, scope, whileNode.Expression));
                Actions.Add(skipCondition);

                ScopeGroup whileGroup = scope.Child();

                ParseBlock(whileGroup, whileGroup, whileNode.Block, false, returnVar);

                // Take the while out of scope.
                whileGroup.Out(this);

                ContinueSkip.SetSkipCount(whileStartIndex);
                Actions.Add(Element.Part <A_Loop>());

                skipCondition.ParameterValues[1] = new V_Number(GetSkipCount(skipCondition));

                ContinueSkip.ResetSkip();
                return;
            }

            // If
            case IfNode ifNode:
            {
                A_SkipIf if_SkipIf = new A_SkipIf()
                {
                    ParameterValues = new IWorkshopTree[2]
                };
                if_SkipIf.ParameterValues[0] = !(ParseExpression(getter, scope, ifNode.IfData.Expression));

                Actions.Add(if_SkipIf);

                var ifScope = scope.Child();

                // Parse the if body.
                ParseBlock(ifScope, ifScope, ifNode.IfData.Block, false, returnVar);

                // Take the if out of scope.
                ifScope.Out(this);

                // 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;

                // Create the "Skip" action.
                A_Skip if_Skip = new A_Skip();
                if (addIfSkip)
                {
                    Actions.Add(if_Skip);
                }

                // Update the initial SkipIf's skip count now that we know the number of actions the if block has.
                if_SkipIf.ParameterValues[1] = new V_Number(GetSkipCount(if_SkipIf));

                // 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()
                    {
                        ParameterValues = new IWorkshopTree[2]
                    };
                    elseif_SkipIf.ParameterValues[0] = !(ParseExpression(getter, scope, ifNode.ElseIfData[i].Expression));

                    Actions.Add(elseif_SkipIf);

                    // Parse the else-if body.
                    var elseifScope = scope.Child();
                    ParseBlock(elseifScope, elseifScope, ifNode.ElseIfData[i].Block, false, returnVar);

                    // Take the else-if out of scope.
                    elseifScope.Out(this);

                    // 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;

                    // Create the "Skip" action for the else-if.
                    if (addIfElseSkip)
                    {
                        elseif_Skips[i] = new A_Skip();
                        Actions.Add(elseif_Skips[i]);
                    }

                    // Set the SkipIf's parameters.
                    elseif_SkipIf.ParameterValues[1] = new V_Number(GetSkipCount(elseif_SkipIf));
                }

                // Parse else body.
                if (ifNode.ElseBlock != null)
                {
                    var elseScope = scope.Child();
                    ParseBlock(elseScope, elseScope, ifNode.ElseBlock, false, returnVar);

                    // Take the else out of scope.
                    elseScope.Out(this);
                }

                // 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 (addIfSkip)
                {
                    if_Skip.ParameterValues = new IWorkshopTree[]
                    {
                        new V_Number(GetSkipCount(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(GetSkipCount(elseif_Skips[i]))
                        };
                    }
                }

                return;
            }

            // Return
            case ReturnNode returnNode:

                if (returnNode.Value != null)
                {
                    Element result = ParseExpression(getter, scope, returnNode.Value);
                    if (returnVar != null)
                    {
                        Actions.AddRange(returnVar.SetVariable(result));
                    }
                }

                A_Skip returnSkip = new A_Skip();
                Actions.Add(returnSkip);
                Actions.Add(Element.Part <A_Skip>(new V_Number(-1)));
                ReturnSkips.Add(returnSkip);
                return;

            case DeleteNode deleteNode:
                DefinedClass.Delete(ParseExpression(getter, scope, deleteNode.Delete), this);
                return;

            // Define
            case DefineNode defineNode:
                ParseDefine(getter, scope, defineNode);
                return;

            case ExpressionTreeNode expressionTree:
                new ParseExpressionTree(this, getter, scope, expressionTree);
                return;

            default:
                throw new SyntaxErrorException("Expected statement.", statement.Location);
            }
        }