/// <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); }
/// <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()); }
/// <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); }