private static bool SimplifyScans(AbstractSyntaxTree root)
        {
            if (root.Count > 1)
            {
                bool RetVal = false;
                foreach (AbstractSyntaxTree ast in root.ChildNodes)
                {
                    RetVal |= SimplifyScans(ast);
                }

                return(RetVal);
            }

            if (root.Count == 1)
            {
                if (root[0].Op == OpCode.AddPtr)
                {
                    root.RemoveAt(0);
                    root.Op = OpCode.ScanRight;
                    return(true);
                }
                if (root[0].Op == OpCode.SubPtr)
                {
                    root.RemoveAt(0);
                    root.Op = OpCode.ScanLeft;
                    return(true);
                }
            }

            return(false);
        }
        private static bool SimplifyAssignZeroLoops(AbstractSyntaxTree root)
        {
            bool RetVal = false;

            bool Predicate(AbstractSyntaxTree ast) =>
            (ast.AllChildrenAssignZero || ast.AllChildrenDecVal || ast.AllChildrenIncVal) && ast.Count % 2 != 0;

            // ToList hack allows the original list to be changed.
            foreach (AbstractSyntaxTree ast in root.ChildNodes)
            {
                RetVal |= SimplifyAssignZeroLoops(ast);

                if (ast.Op == OpCode.Loop && Predicate(ast))
                {
                    OpCode op = ast[0].Op;
                    if (op == OpCode.AssignZero || op.ModifiesValue())
                    {
                        ast.ChildNodes.RemoveAt(0);
                        ast.Op = OpCode.AssignZero;
                        RetVal = true;
                    }
                }
            }

            return(RetVal);
        }
        private static bool EliminateDeadStores(AbstractSyntaxTree root, bool trueRoot)
        {
            bool RetVal = false;

            for (int i = root.Count - 2; i >= 0; i--)
            {
                RetVal |= EliminateDeadStores(root[i], false);
                if (root[i + 1].Op == OpCode.AssignZero || root[i + 1].Op == OpCode.AssignVal)
                {
                    AbstractSyntaxTree ast = root[i];
                    if (ast.Op.AssignsValue() || ast.Op == OpCode.AssignZero || ast.Op.ModifiesValue())
                    {
                        root.Remove(ast);
                    }
                }
            }

            if (root.Count > 0 && root[0].Op == OpCode.AssignZero && trueRoot)
            {
                root.RemoveAt(0);
                return(true);
            }

            return(RetVal);
        }
        private static bool EliminateUnreachableLoops(AbstractSyntaxTree root, bool trueRoot)
        {
            bool RetVal = false;

            for (int i = 1; i < root.Count; i++)
            {
                RetVal |= EliminateUnreachableLoops(root[i], false);
                if (root[i].Op == OpCode.Loop && (root[i - 1].Op == OpCode.Loop || root[i - 1].Op == OpCode.AssignZero))
                {
                    root.RemoveAt(i);
                    RetVal = true;
                    i--;
                }
            }

            if (trueRoot)
            {
                if (root.Count > 0 && root[0].Op == OpCode.Loop)
                {
                    root.RemoveAt(0);
                    return(true);
                }

                if (root.Count == 0 && root.Op == OpCode.Loop)
                {
                    root.Op = OpCode.Nop;
                    return(true);
                }
            }

            return(RetVal);
        }
        /// <summary>
        /// Converts given user code into a IL program which can then be compiled.
        /// </summary>
        /// <param name="settings">The settings.</param>
        /// <returns></returns>
        public static (ErrorCodes errorCode, List <Instruction> Il) CompileAst(CompilerSettings settings)
        {
            AbstractSyntaxTree tree = Lexer.LexAst(settings.InputCode);

            Optimizer.Optimize(tree, settings);
            List <Instruction> Il = tree.ToIl();

            Optimizer.Optimize(Il, settings);
            return(ErrorCodes.Successful, Il);
        }
        internal static bool EliminateConflictingInstructions(AbstractSyntaxTree ast)
        {
            bool RetVal = false;

            if (ast.Count > 0)
            {
                RetVal |= EliminateConflictingInstructions(ast[0]);
            }

            for (int i = 1; i < ast.Count; i++)
            {
                RetVal |= EliminateConflictingInstructions(ast[i]);
                if (ast[i - 1].Op.IsReversable() && ast[i - 1].Op.GetReversedOpCode() == ast[i].Op)
                {
                    ast.RemoveAt(i);
                    ast.RemoveAt(i - 1);
                    i     += 2;
                    RetVal = true;
                }
            }

            return(RetVal);
        }
        internal static void Optimize(AbstractSyntaxTree ast, CompilerSettings settings)
        {
            bool Continue = true;

            while (Continue)
            {
                Continue = false;
                if (settings.SimplifyAssignZeroLoops)
                {
                    Continue |= SimplifyAssignZeroLoops(ast);
                    ast.ColapseNops();
                }

                if (settings.EliminateDeadStores)
                {
                    Continue |= EliminateDeadStores(ast, true);
                    ast.ColapseNops();
                }

                if (settings.EliminateUnreachableLoops)
                {
                    Continue |= EliminateUnreachableLoops(ast, true);
                    ast.ColapseNops();
                }

                if (settings.EliminateConflictingInstructions)
                {
                    Continue |= EliminateConflictingInstructions(ast);
                    ast.ColapseNops();
                }
                if (true)
                {
                    Continue |= SimplifyScans(ast);
                    ast.ColapseNops();
                }
            }
        }
        private static AbstractSyntaxTree GenerateTree(ref int index, string code, bool isRoot, OpCode bundleType)
        {
            AbstractSyntaxTree tree = new AbstractSyntaxTree(OpCode.Loop);

            while (index < code.Length)
            {
                switch (code[index++])
                {
                case '[':
                    tree.Add(GenerateTree(ref index, code, false, OpCode.StartLoop));
                    break;

                case ']':
                    if (isRoot)
                    {
                        throw new InvalidOperationException();     // Unbalanced ']'
                    }

                    if (bundleType != OpCode.StartLoop)
                    {
                        index--;
                    }

                    return(tree);

                case '+':
                    tree.Add(new AbstractSyntaxTree(OpCode.AddVal));
                    break;

                case '-':
                    tree.Add(new AbstractSyntaxTree(OpCode.SubVal));
                    break;

                case '>':
                    tree.Add(new AbstractSyntaxTree(OpCode.AddPtr));
                    break;

                case '<':
                    tree.Add(new AbstractSyntaxTree(OpCode.SubPtr));
                    break;

                case '.':
                    tree.Add(new AbstractSyntaxTree(OpCode.SetOutput));
                    break;

                case ',':
                    tree.Add(new AbstractSyntaxTree(OpCode.GetInput));
                    break;

                default:
                    throw new InvalidOperationException();
                }
            }

            if (!isRoot)
            {
                throw new InvalidOperationException(); // Unbalanced '['
            }

            tree.Op = OpCode.Nop;

            return(tree);
        }