예제 #1
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)
        {
            // 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));
        }
예제 #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)
        {
            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);
        }