Exemplo n.º 1
0
            public CondIntermediate ExtractInstrArgs(Instr instr)
            {
                CmdCond cmd = new CmdCond {
                    Name = Doc.Name
                };

                if (instr.Layers != null)
                {
                    throw new ArgumentException($"Cannot decompile {instr} because it's a control flow instruction with a layer");
                }
                List <int> ignore = new List <int>();

                if (ControlArg >= 0)
                {
                    ignore.Add(ControlArg);
                    if (instr.Args[ControlArg] is ParamArg)
                    {
                        throw new FancyNotSupportedException($"Control arg {ControlArg} in comes from event params, cannot decompile", instr);
                    }
                }
                Cond retCond = cmd;

                if (CompareArg >= 0)
                {
                    ignore.Add(NegateArg);
                    ignore.Add(CompareArg);
                    CompareCond cmp = new CompareCond();
                    cmp.Type = (ComparisonType)ExtractIntArg(instr, NegateArg);
                    cmp.Rhs  = instr.Args[CompareArg];
                    if (CompareArg2 >= 0)
                    {
                        ignore.Add(CompareArg2);
                        cmp.Lhs = instr.Args[CompareArg2];
                        // All args should be ignored at this point
                    }
                    else
                    {
                        cmp.CmdLhs = cmd;
                    }
                    retCond = cmp;
                }
                else if (NegateArg >= 0)
                {
                    ignore.Add(NegateArg);
                    cmd.Negate = ExtractIntArg(instr, NegateArg) != TrueOp;
                }
                foreach (KeyValuePair <int, int> req in ExtraArgs)
                {
                    ignore.Add(req.Key);
                }
                for (int i = 0; i < instr.Args.Count; i++)
                {
                    if (!ignore.Contains(i))
                    {
                        cmd.Args.Add(instr.Args[i]);
                    }
                }
                // Hide default optional arguments here, all-or-nothing. This makes for nicer output.
                if (Doc.OptionalArgs > 0)
                {
                    int hidable = 0;
                    for (hidable = 0; hidable < Doc.OptionalArgs; hidable++)
                    {
                        int pos = Doc.Args.Count - 1 - hidable;
                        // If arg exists and is default value, it can be hidden
                        if (pos >= cmd.Args.Count)
                        {
                            break;
                        }
                        if (!TryExtractIntArg(cmd.Args[pos], out int arg) || arg.ToString() != Doc.Args[pos].Default.ToString())
                        {
                            break;
                        }
                    }
                    if (hidable == Doc.OptionalArgs)
                    {
                        cmd.Args.RemoveRange(Doc.Args.Count - Doc.OptionalArgs, Doc.OptionalArgs);
                    }
                }
                CondIntermediate ret;

                if (Variant == ControlType.COND)
                {
                    int reg = ExtractIntArg(instr, ControlArg);
                    ret = new CondAssign
                    {
                        Cond   = retCond,
                        ToCond = reg,
                        Op     = reg == 0 ? CondAssignOp.Assign : (reg > 0 ? CondAssignOp.AssignAnd : CondAssignOp.AssignOr),
                    };
                }
                else if (Variant == ControlType.SKIP)
                {
                    ret = new Goto
                    {
                        Cond      = retCond,
                        SkipLines = ExtractIntArg(instr, ControlArg)
                    };
                }
                else if (Variant == ControlType.END)
                {
                    ret = new End
                    {
                        Cond = retCond,
                        Type = ExtractIntArg(instr, ControlArg)
                    };
                }
                else if (Variant == ControlType.GOTO)
                {
                    ret = new Goto
                    {
                        Cond    = retCond,
                        ToLabel = $"L{ExtractIntArg(instr, ControlArg)}"
                    };
                }
                else if (Variant == ControlType.WAIT)
                {
                    ret = new Wait
                    {
                        Cond    = retCond,
                        Special = true
                    };
                }
                else
                {
                    throw new ArgumentException($"Unrecognized variant style {Variant}");
                }
                return(ret);
            }
Exemplo n.º 2
0
        // As part of instruction compilation, rewrite all instruction so that they correspond to valid emevd commands.
        // But don't convert them into instructions yet, since we need to edit control things like condition group register allocation and line skipping.
        public List <Intermediate> ExpandCond(Intermediate im, Func <string> newVar)
        {
            if (im is Instr || im is Label || im is NoOp || im is JSStatement)
            {
                return(new List <Intermediate> {
                    im
                });
            }
            else if (im is CondIntermediate condIm)
            {
                // Either use a direct command, or require a condition group because of negation or missing variant (or, in future, optional args).
                // Some commands do not support condition groups:
                // - goto/skip/end only: CompareCompiledConditionGroup, CompareNumberOfCoopClients, CompareNumberOfCoopClients
                // - goto only: HollowArenaMatchType
                Cond cond = condIm.Cond;
                if (cond == null)
                {
                    return(new List <Intermediate> {
                        im
                    });
                }
                else if (cond is ErrorCond)
                {
                    // Just quit out here. Error is already recorded elsewhere
                    return(new List <Intermediate>());
                }
                else if (cond is OpCond)
                {
                    throw new Exception($"Internal error: should have expanded out all subconditions in {im}");
                }

                string docName = cond.DocName;
                if (!CondDocs.TryGetValue(docName, out FunctionDoc functionDoc))
                {
                    throw new Exception($"Internal error: Unknown condition function {docName}");
                }
                ControlType type = condIm.ControlType;
                // The only non-synthetic use of WAIT is in condition groups, and these can just be converted to main group eval with no behavior change.
                if (type == ControlType.WAIT)
                {
                    type = ControlType.COND;
                }
                bool indirection = false;
                if (functionDoc.Variants.ContainsKey(type))
                {
                    // If variant exists, see if it can be used
                    ConditionVariant mainVariant = GetVariantByName(docName, functionDoc.Variants[type]);
                    if (cond.Negate && mainVariant.NegateArg == -1)
                    {
                        if (!functionDoc.Variants.ContainsKey(ControlType.COND))
                        {
                            throw new FancyNotSupportedException($"Can't use {docName} in this form since it does't have a condition version. Add or remove negation so that it can be translated to emevd.", im);
                        }
                        // Console.WriteLine($"Expanding {im} because it can't be negated as {type}");
                        indirection = true;
                    }
                }
                else
                {
                    // Needed for DS1, which only has skip. The instruction is rewritten later with # of lines if the label is synthetic.
                    if (type == ControlType.GOTO && functionDoc.Variants.ContainsKey(ControlType.SKIP))
                    {
                        // The type is ControlType.SKIP in this case, which will be accounted for once SkipLines is filled in.
                    }
                    else if (functionDoc.Variants.ContainsKey(ControlType.COND))
                    {
                        indirection = true;
                    }
                    else
                    {
                        // Also part of error: can't use compiled groups in condition group.
                        throw new FancyNotSupportedException($"Can't use statement type {type} with {functionDoc.Name}; can only use it with {string.Join(", ", functionDoc.Variants.Keys)}", im);
                    }
                }
                if (indirection)
                {
                    string           var        = newVar() + "z";
                    ConditionVariant cmdVariant = GetVariantByName(docName, functionDoc.Variants[ControlType.COND]);
                    CondRef          tmpCond    = new CondRef {
                        Compiled = false, Name = var
                    };
                    if (cond.Negate && cmdVariant.NegateArg == -1)
                    {
                        cond.Negate    = false;
                        tmpCond.Negate = true;
                    }
                    Intermediate instr = new CondAssign {
                        Cond = cond, Op = CondAssignOp.Assign, ToVar = var, Labels = condIm.Labels
                    };

                    // Note that this is destructive. If this becomes a problem with multiple references that should
                    // remain divergent, conditions may need some deep cloning routines.
                    condIm.Cond   = tmpCond;
                    condIm.Labels = new List <string>();
                    return(new List <Intermediate> {
                        instr, condIm
                    });
                }
                else
                {
                    return(new List <Intermediate> {
                        condIm
                    });
                }
            }
            else
            {
                throw new Exception($"Internal error: unable to compile unknown instruction {im}");
            }
        }