/// <summary>
        /// Gets the translated code for the grammar structure.
        /// </summary>
        /// <returns>The translated code for the grammar structure.</returns>
        /// <remarks>
        /// Uses the delete and add method, as demonstrated in this project:
        /// https://scratch.mit.edu/projects/118629266/
        /// </remarks>
        public IEnumerable <Block> Translate(TranslationContext context)
        {
            // Find declaration

            IDeclaration declaration = context.GetDeclaration(ArrayName);

            if (declaration == null)
            {
                context.ErrorList.Add(new CompilerError($"Array '{ArrayName}' is not defined", ErrorType.NotDefined, ErrorToken, FileName));
                return(Enumerable.Empty <Block>());
            }

            // Try as scoped array

            if (declaration is StackValue scopedArray)
            {
                if (scopedArray.StackSpace == 1)
                {
                    context.ErrorList.Add(new CompilerError($"Object '{ArrayName}' is not an array", ErrorType.ImproperUsage, ErrorToken, FileName));
                    return(Enumerable.Empty <Block>());
                }

                List <Block> scopedBlocks = new List <Block>(Items.Count);

                for (int i = 0; i < Items.Count; i++)
                {
                    scopedBlocks.AddRange(scopedArray.CreateArrayAssignment(context, Items[i], new TerminalExpression(i)));
                }

                return(scopedBlocks);
            }

            // Try as global list

            GlobalListDeclaration globalList = declaration as GlobalListDeclaration;

            if (globalList == null)
            {
                // Neither scoped array or global list
                context.ErrorList.Add(new CompilerError($"Object '{ArrayName}' is not an array", ErrorType.ImproperUsage, ErrorToken, FileName));
                return(Enumerable.Empty <Block>());
            }

            List <Block> globalBlocks = new List <Block>(1 + Items.Count)
            {
                new Block(BlockSpecs.DeleteItemOfList, "all", ArrayName)
            };

            foreach (IExpression item in Items)
            {
                globalBlocks.AddRange(new BlockBuilder(BlockSpecs.AddToList, context)
                                      .AddParam(item, globalList.Type)
                                      .AddParam(ArrayName)
                                      .Create());
            }

            return(globalBlocks);
        }
Exemple #2
0
        /// <summary>
        /// Gets the translated code for the grammar structure.
        /// </summary>
        /// <returns>The translated code for the grammar structure.</returns>
        public IEnumerable <Block> Translate(TranslationContext context)
        {
            // Get generic declaration

            IDeclaration declaration = context.GetDeclaration(ArrayName);

            if (declaration == null)
            {
                context.ErrorList.Add(new CompilerError($"'{ArrayName}' is not defined", ErrorType.NotDefined,
                                                        ErrorToken, FileName));
                return(Enumerable.Empty <Block>());
            }

            // Scoped arrays

            if (declaration is StackValue scopedArray)
            {
                Operator.TestCompatible(scopedArray.Type, context, FileName, ErrorToken);

                switch (Operator)
                {
                case AssignOperator.Equals:
                    return(scopedArray.CreateArrayAssignment(context, Value, Index));

                case AssignOperator.AddEquals:
                    return(scopedArray.CreateArrayAssignment(context,
                                                             new CompoundExpression(CompoundOperator.Plus,
                                                                                    new ArrayLookupExpression(scopedArray, Index, FileName, ErrorToken), Value, FileName,
                                                                                    ErrorToken), Index));

                case AssignOperator.MinusEquals:
                    return(scopedArray.CreateArrayAssignment(context,
                                                             new CompoundExpression(CompoundOperator.Minus,
                                                                                    new ArrayLookupExpression(scopedArray, Index, FileName, ErrorToken), Value, FileName,
                                                                                    ErrorToken), Index));

                case AssignOperator.DotEquals:
                    return(scopedArray.CreateArrayAssignment(context,
                                                             new CompoundExpression(CompoundOperator.Concat,
                                                                                    new ArrayLookupExpression(scopedArray, Index, FileName, ErrorToken), Value, FileName,
                                                                                    ErrorToken), Index));

                case AssignOperator.PlusPlus:
                    return(scopedArray.CreateArrayAssignment(context,
                                                             new CompoundExpression(CompoundOperator.Plus,
                                                                                    new ArrayLookupExpression(scopedArray, Index, FileName, ErrorToken), new TerminalExpression(1, DataType.Number),
                                                                                    FileName, ErrorToken), Index));

                case AssignOperator.MinusMinus:
                    return(scopedArray.CreateArrayAssignment(context,
                                                             new CompoundExpression(CompoundOperator.Minus,
                                                                                    new ArrayLookupExpression(scopedArray, Index, FileName, ErrorToken), new TerminalExpression(1, DataType.Number),
                                                                                    FileName, ErrorToken), Index));

                default:
                    throw new ArgumentOutOfRangeException();
                }
            }

            // Global arrays

            GlobalListDeclaration globalArray = declaration as GlobalListDeclaration;

            if (globalArray == null)
            {
                // Not an array
                context.ErrorList.Add(new CompilerError($"Object '{ArrayName}' is not an array", ErrorType.ImproperUsage, ErrorToken, FileName));
                return(Enumerable.Empty <Block>());
            }

            Operator.TestCompatible(globalArray.Type, context, FileName, ErrorToken);

            IExpression value;

            switch (Operator)
            {
            case AssignOperator.Equals:
                DataType valueType = Value.GetReturnType(context);
                if (!globalArray.Type.IsCompatible(valueType))
                {
                    context.ErrorList.Add(new CompilerError(
                                              $"Expected value of type '{globalArray.Type}' but instead found value of type '{valueType}'",
                                              ErrorType.TypeMismatch, Value.ErrorToken, Value.FileName));
                }

                value = Value;
                break;

            case AssignOperator.AddEquals:
                value = new CompoundExpression(CompoundOperator.Plus,
                                               new ArrayLookupExpression(ArrayName, Index, FileName, ErrorToken),
                                               Value, FileName, ErrorToken);
                break;

            case AssignOperator.MinusEquals:
                value = new CompoundExpression(CompoundOperator.Minus,
                                               new ArrayLookupExpression(ArrayName, Index, FileName, ErrorToken),
                                               Value, FileName, ErrorToken);
                break;

            case AssignOperator.DotEquals:
                value = new CompoundExpression(CompoundOperator.Multiply,
                                               new ArrayLookupExpression(ArrayName, Index, FileName, ErrorToken), Value, FileName, ErrorToken);
                break;

            case AssignOperator.PlusPlus:
                value = new CompoundExpression(CompoundOperator.Plus,
                                               new ArrayLookupExpression(ArrayName, Index, FileName, ErrorToken),
                                               new TerminalExpression(1, DataType.Number), FileName, ErrorToken);
                break;

            case AssignOperator.MinusMinus:
                value = new CompoundExpression(CompoundOperator.Minus,
                                               new ArrayLookupExpression(ArrayName, Index, FileName, ErrorToken),
                                               new TerminalExpression(1, DataType.Number), FileName, ErrorToken);
                break;

            default:
                throw new ArgumentOutOfRangeException();
            }

            return(new BlockBuilder(BlockSpecs.ReplaceItemOfList, context)
                   .AddParam(Index)
                   .AddParam(ArrayName)
                   .AddParam(value)
                   .Create());
        }
Exemple #3
0
        /// <summary>
        /// Gets the translated code for the grammar structure.
        /// </summary>
        /// <returns>The translated code for the grammar structure.</returns>
        public IEnumerable <Block> Translate(TranslationContext context)
        {
            List <Block> output = new List <Block>();

            // Create scope of loop
            Scope innerScope = new Scope(context.CurrentScope);
            TranslationContext newContext = new TranslationContext(innerScope, context);

            // TODO: Inbuilt foreach block optimisation
            // TODO: Inline foreach

            // Create counter variable
            StackValue internalCounter = context.CurrentScope.CreateStackValue();

            output.AddRange(internalCounter.CreateDeclaration(1));

            // Create item variable
            StackValue itemVar = new StackValue(Variable, VarType, false);

            innerScope.StackValues.Add(itemVar);
            output.AddRange(itemVar.CreateDeclaration(VarType.GetDefault()));

            List <Block> loopContents = new List <Block>();

            GlobalListDeclaration globalList =
                context.CurrentSprite.GetList(SourceName) ?? context.Project.GetList(SourceName);
            StackValue arrayValue = null;

            if (globalList != null)
            {
                // Translate loop contents
                loopContents.Add(itemVar.CreateVariableAssignment(new Block(BlockSpecs.GetItemOfList,
                                                                            internalCounter.CreateVariableLookup(), SourceName)));
            }
            else
            {
                // Get stackvalue for array
                arrayValue = context.CurrentScope.Search(SourceName);

                if (arrayValue == null)
                {
                    context.ErrorList.Add(new CompilerError($"Array '{SourceName}' is not defined", ErrorType.NotDefined,
                                                            ErrorToken, FileName));
                    return(Enumerable.Empty <Block>());
                }

                loopContents.Add(itemVar.CreateVariableAssignment(arrayValue.CreateArrayLookup(internalCounter.CreateVariableLookup())));
            }

            // Increment counter
            loopContents.Add(internalCounter.CreateVariableIncrement(1));

            // Translate loop main body
            foreach (IEnumerable <Block> translated in Statements.Select(x => x.Translate(newContext)))
            {
                loopContents.AddRange(translated);
            }

            // Create loop Scratch block
            object repeats = globalList != null ? new Block(BlockSpecs.LengthOfList, SourceName) : (object)arrayValue.StackSpace;

            output.Add(new Block(BlockSpecs.Repeat, repeats, loopContents.ToArray()));

            // Clean up scope
            output.AddRange(internalCounter.CreateDestruction());
            output.AddRange(itemVar.CreateDestruction());

            return(output);
        }