/// <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) { // Find method MethodDeclaration customMethod = context.CurrentSprite.GetMethod(MethodName, Parameters.Count); if (customMethod != null) { // Custom method found // Don't check if returns value - it does not matter here // TODO: inline List <object> translatedParams = new List <object> { customMethod.GetInternalName() }; translatedParams.AddRange(Parameters.Select(x => x.Balance().Translate(context))); for (int i = Parameters.Count; i < customMethod.Params.Count; i++) { translatedParams.Add(customMethod.Params[i].Default); } translatedParams.Add(new Block(BlockSpecs.GetParameter, Settings.StackRefParam)); translatedParams.Add(new Block(BlockSpecs.LengthOfList, Settings.StackIdentifier)); return(new[] { new Block(BlockSpecs.CustomMethodCall, translatedParams.ToArray()) }); } // Custom method doesn't exist, so search inbuilt methods if (InbuiltMethods.StandardMethods.TryGetValue(MethodName, out MethodSignature inbuiltMethod)) { // Inbuilt method found // Check method is not a reporter if (inbuiltMethod.IsReporter) { context.ErrorList.Add(new CompilerError( $"The inbuilt method '{MethodName}' can only be used as an input", ErrorType.ImproperUsage, ErrorToken, FileName)); return(Enumerable.Empty <Block>()); } // Check parameter count is valid if (Parameters.Count == inbuiltMethod.Inputs.Length) { return new[] { new Block(inbuiltMethod.Name, Parameters.Select(x => x.Balance().Translate(context)).ToArray()) } } ; // Error: Invalid parameters context.ErrorList.Add(new CompilerError( $"Expected inputs '{string.Join("', '", inbuiltMethod.Inputs)}'", ErrorType.InvalidArgument, ErrorToken, FileName)); return(Enumerable.Empty <Block>()); } // Try non standard blocks if (TranslateNonStandardMethod(context, false) is Block nonStandardMethod) { return new[] { nonStandardMethod } } ; // Error - nethod not found context.ErrorList.Add(new CompilerError($"Method '{MethodName}' is not defined", ErrorType.NotDefined, 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> object ICompilable <object> .Translate(TranslationContext context) { // Find method MethodDeclaration customMethod = context.CurrentSprite.GetMethod(MethodName, Parameters.Count); if (customMethod != null) { // Custom method found // Ensure it returns a value if (!customMethod.HasReturn) { context.ErrorList.Add(new CompilerError($"Method '{MethodName}' does not return a value", ErrorType.ImproperUsage, ErrorToken, FileName)); return(null); } // TODO: inline // TODO: Optimise to not need additional stack value (except for repeat until) List <object> translatedParams = new List <object> { customMethod.GetInternalName() }; translatedParams.AddRange(Parameters.Select(x => x.Translate(context))); for (int i = Parameters.Count; i < customMethod.Params.Count; i++) { translatedParams.Add(customMethod.Params[i].Default); } translatedParams.Add(new Block(BlockSpecs.GetParameter, Settings.StackRefParam)); translatedParams.Add(new Block(BlockSpecs.LengthOfList, Settings.StackIdentifier)); context.Before.Add(new Block(BlockSpecs.CustomMethodCall, translatedParams.ToArray())); StackValue returnValue = context.CurrentScope.CreateStackValue(); context.Before.Add(returnValue.CreateDeclaration(new Block(BlockSpecs.GetVariable, customMethod.GetReturnVariableName())).First()); return(returnValue.CreateVariableLookup()); } // Custom method doesn't exist, so search inbuilt methods if (InbuiltMethods.StandardMethods.TryGetValue(MethodName, out MethodSignature inbuiltMethod)) { // Inbuilt method found // Check method is a reporter if (!inbuiltMethod.IsReporter) { context.ErrorList.Add(new CompilerError( $"The inbuilt method '{MethodName}' does not return a value", ErrorType.ImproperUsage, ErrorToken, FileName)); return(new Block(null)); } // Check parameter count is valid if (Parameters.Count == inbuiltMethod.Inputs.Length) { return(new Block(inbuiltMethod.Name, Parameters.Select(x => x.Translate(context)).ToArray())); } // Parameter count not valid context.ErrorList.Add(new CompilerError( $"Expected inputs '{string.Join("', '", inbuiltMethod.Inputs)}'", ErrorType.InvalidArgument, ErrorToken, FileName)); return(new Block(null)); } // Try non standard blocks object nonStandardMethod = TranslateNonStandardMethod(context, true); if (nonStandardMethod != null) { return(nonStandardMethod); } // Error - nethod not found context.ErrorList.Add(new CompilerError($"Method '{MethodName}' is not defined", ErrorType.NotDefined, ErrorToken, FileName)); return(new Block(null)); }
/// <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); }