public static CanonizedExp Combine(CanonizedExp b1, CanonizedExp b2,
                                           Func <TreeExp, TreeExp, TreeExp> f)
        {
            CanonizedExp c = Compose(b1, b2.Body);

            return(new CanonizedExp(c.Body, f(c.Exp, b2.Exp)));
        }
        public static List <TreeStm> ToStm(CanonizedExp b1, CanonizedExp b2, Func <TreeExp, TreeExp, TreeStm> f)
        {
            CanonizedExp   c   = Compose(b1, b2.Body);
            List <TreeStm> res = new List <TreeStm>(c.Body);

            res.Add(f(c.Exp, b2.Exp));
            return(res);
        }
        private CanonizedExp CanonExpression(TreeExp exp)
        {
            switch (exp)
            {
            case ExpCall call:
            {
                CanonizedExp cfunc = CanonNoTopCall(call.Function);

                List <CanonizedExp> cargs = new List <CanonizedExp>();
                foreach (var arg in call.Args)
                {
                    cargs.Add(CanonNoTopCall(arg));
                }

                return(CanonizedExp.Combine(cfunc, cargs, (a, b) => new ExpCall(a, b)));
            }

            case ExpConst constant:
            {
                return(new CanonizedExp(constant));
            }

            case ExpName name:
            {
                return(new CanonizedExp(name));
            }

            case ExpTemp temp:
            {
                return(new CanonizedExp(temp));
            }

            case ExpParam para:
            {
                return(new CanonizedExp(para));
            }

            case ExpMem mem:
            {
                return(CanonNoTopCall(mem.Address).MapExp((a) => new ExpMem(a)));
            }

            case ExpBinOp binop:
            {
                return(CanonizedExp.Combine(CanonNoTopCall(binop.Left), CanonNoTopCall(binop.Right),
                                            (l, r) => new ExpBinOp(binop.Operator, l, r)));
            }

            case ExpESeq eseq:
            {
                CanonizedExp b1   = new CanonizedExp(CanonStm(eseq.Stm), new ExpConst(0));
                CanonizedExp cres = CanonNoTopCall(eseq.Exp);
                return(CanonizedExp.Combine(b1, cres, (nop, e) => e));
            }

            default: throw new Exception("Shiiiit");
            }
        }
        CanonizedExp CanonNoTopCall(TreeExp e)
        {
            CanonizedExp ce = CanonExpression(e);

            if (ce.Exp is ExpCall)
            {
                TreeExp        call = ce.Exp;
                TreeExp        t    = new ExpTemp(new Temp());
                List <TreeStm> stms = new List <TreeStm>(ce.Body);
                stms.Add(new StmMove(t, call));
                ce = new CanonizedExp(stms, t);
            }
            return(ce);
        }
        // cfunc, cargs, (a ,b) => new ExpCall(a, b)
        public static CanonizedExp Combine(CanonizedExp b1, List <CanonizedExp> clist,
                                           Func <TreeExp, List <TreeExp>, TreeExp> f)
        {
            List <CanonizedExp> clistRev = new List <CanonizedExp>(clist);

            clistRev.Reverse();

            List <TreeExp> joined = new List <TreeExp>();
            List <TreeStm> stms   = new List <TreeStm>();

            foreach (CanonizedExp ce in clistRev)
            {
                CanonizedExp ca = Compose(ce, stms);
                stms = ca.Body;
                joined.Insert(0, ca.Exp);
            }
            CanonizedExp c = Compose(b1, stms);

            return(new CanonizedExp(c.Body, f(c.Exp, joined)));
        }
        private static CanonizedExp Compose(CanonizedExp c, List <TreeStm> stms)
        {
            if (!stms.Any())
            {
                return(c);
            }

            List <TreeStm> newstms = new List <TreeStm>();

            newstms.AddRange(c.Body);

            if (Commute(stms, c.Exp))
            {
                newstms.AddRange(stms);
                return(new CanonizedExp(newstms, c.Exp));
            }
            else
            {
                TreeExp t = new ExpTemp(new Temp());
                newstms.Add(new StmMove(t, c.Exp));
                newstms.AddRange(stms);
                return(new CanonizedExp(newstms, t));
            }
        }
        private List <TreeStm> CanonStm(TreeStm s)
        {
            switch (s)
            {
            case StmMove move:
            {
                if (move.Dest is ExpMem mem)
                {
                    TreeExp addr = ((ExpMem)move.Dest).Address;
                    CanonExpression(addr);
                    return(CanonizedExp.ToStm(CanonNoTopCall(addr), CanonNoTopCall(move.Source),
                                              (eaddr, esrc) => new StmMove(new ExpMem(eaddr), esrc)));
                }
                else if (move.Dest is ExpTemp)
                {
                    return(CanonExpression(move.Source).ToStm(esrc => new StmMove(move.Dest, esrc)));
                }
                else if (move.Dest is ExpParam)
                {
                    return(CanonExpression(move.Source).ToStm(esrc => new StmMove(move.Dest, esrc)));
                }
                else if (move.Dest is ExpESeq dst)
                {
                    return(CanonStm(new StmSeq(new List <TreeStm> {
                            dst.Stm, new StmMove(dst.Exp, move.Source)
                        })));
                }
                else
                {
                    throw new Exception("Left-hand side of MOVE must be TEMP, PARAM, MEM or ESEQ.");
                }
            }

            case StmJump jump:
            {
                return(CanonNoTopCall(jump.Dest).ToStm(e => new StmJump(e, jump.PossibleTargets)));
            }

            case StmCJump cjump:
            {
                return(CanonizedExp.ToStm(CanonNoTopCall(cjump.Left), CanonNoTopCall(cjump.Right),
                                          (l, r) => new StmCJump(cjump.Rel, l, r, cjump.LabelTrue, cjump.LabelFalse)));
            }

            case StmSeq seq:
            {
                List <TreeStm> cstms = new List <TreeStm>();
                foreach (var stat in seq.Stms)
                {
                    cstms.AddRange(CanonStm(stat));
                }
                return(cstms);
            }

            case StmLabel label:
            {
                List <TreeStm> stms = new List <TreeStm>();
                stms.Add(label);
                return(stms);
            }

            default: throw new Exception("Ya know, did not match any Statement.");
            }
        }