/// <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 variable IDeclaration variable = context.GetDeclaration(VariableName); // Check variable was found if (variable == null) { context.ErrorList.Add(new CompilerError($"Variable '{VariableName}' was not defined", ErrorType.NotDefined, ErrorToken, FileName)); return(Enumerable.Empty <Block>()); } // Try as stack variable StackValue stackValue = variable as StackValue; if (stackValue != null && stackValue.StackSpace == 1) { Operator.TestCompatible(stackValue.Type, context, FileName, ErrorToken); switch (Operator) { case AssignOperator.Equals: return(stackValue.CreateVariableAssignment(context, Value)); case AssignOperator.AddEquals: return(stackValue.CreateVariableIncrement(context, Value)); case AssignOperator.MinusEquals: return(stackValue.CreateVariableIncrement(context, new UnaryExpression(Value, UnaryOperator.Minus, FileName, ErrorToken))); case AssignOperator.DotEquals: return(stackValue.CreateVariableAssignment(context, new CompoundExpression(CompoundOperator.Concat, new LookupExpression(stackValue, FileName, ErrorToken), Value, FileName, ErrorToken))); case AssignOperator.PlusPlus: return(new[] { stackValue.CreateVariableIncrement(1) }); case AssignOperator.MinusMinus: return(new[] { stackValue.CreateVariableIncrement(-1) }); default: throw new ArgumentOutOfRangeException(); } } // Try as global variable GlobalVarDeclaration globalVarDeclaration = variable as GlobalVarDeclaration; if (globalVarDeclaration != null) { Operator.TestCompatible(globalVarDeclaration.Type, context, FileName, ErrorToken); switch (Operator) { case AssignOperator.Equals: return(new BlockBuilder(BlockSpecs.SetVariableTo, context) .AddParam(VariableName) .AddParam(Value, globalVarDeclaration.Type) .Create()); case AssignOperator.AddEquals: return(new BlockBuilder(BlockSpecs.ChangeVarBy, context) .AddParam(VariableName) .AddParam(Value) .Create()); case AssignOperator.MinusEquals: return(new BlockBuilder(BlockSpecs.ChangeVarBy, context) .AddParam(VariableName) .AddParam(new UnaryExpression(Value, UnaryOperator.Minus, FileName, ErrorToken)) .Create()); case AssignOperator.DotEquals: return(new BlockBuilder(BlockSpecs.SetVariableTo, context) .AddParam(VariableName).AddParam( new CompoundExpression(CompoundOperator.Concat, new LookupExpression(globalVarDeclaration, FileName, ErrorToken), Value, FileName, ErrorToken)) .Create()); case AssignOperator.PlusPlus: return(new BlockBuilder(BlockSpecs.ChangeVarBy, context) .AddParam(VariableName) .AddParam(1) .Create()); case AssignOperator.MinusMinus: return(new BlockBuilder(BlockSpecs.ChangeVarBy, context) .AddParam(VariableName) .AddParam(-1) .Create()); default: throw new ArgumentOutOfRangeException(); } } // Try as any readonly type if (variable is ParamDeclaration || variable is ConstDeclaration) { context.ErrorList.Add(new CompilerError($"Value '{VariableName}' is read-only", ErrorType.ValueIsReadonly, ErrorToken, FileName)); return(Enumerable.Empty <Block>()); } // Fail context.ErrorList.Add(new CompilerError($"'{VariableName}' is not a variable", ErrorType.ImproperUsage, ErrorToken, FileName)); return(Enumerable.Empty <Block>()); }
/// <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); }