export type Program = Ins[];
export type Ins = Ops | number;

// NOTICE: keep up to date with runtime/arch.hpp

export type Ops = typeof Ops;
export const Ops = {
    Nop: 0x00,
    PushNull: 0x01,
    PushInt: 0x02,
    PushBool: 0x03,
    PushString: 0x04,
    PushPtr: 0x05,
    Pop: 0x06,
    ReserveStatic: 0x07,
    LoadStatic: 0x08,
    StoreStatic: 0x09,
    LoadLocal: 0x0a,
    StoreLocal: 0x0b,
    Call: 0x0c,
    Return: 0x0d,
    Jump: 0x0e,
    JumpIfTrue: 0x0f,
    Builtin: 0x10,
    Add: 0x20,
    Subtract: 0x21,
    Multiply: 0x22,
    Divide: 0x23,
    Remainder: 0x24,
    Equal: 0x25,
    LessThan: 0x26,
    And: 0x27,
    Or: 0x28,
    Xor: 0x29,
    Not: 0x2a,
    SourceMap: 0x30,
} as const;

export type Builtins = typeof Builtins;
export const Builtins = {
    StringConcat: 0x10,
    StringEqual: 0x11,
    ArraySet: 0x20,
    StructSet: 0x30,
    Print: 0x40,
} as const;

export function opToString(op: number): string {
    switch (op) {
        case Ops.Nop:
            return "Nop";
        case Ops.PushNull:
            return "PushNull";
        case Ops.PushInt:
            return "PushInt";
        case Ops.PushBool:
            return "PushBool";
        case Ops.PushString:
            return "PushString";
        case Ops.PushPtr:
            return "PushPtr";
        case Ops.Pop:
            return "Pop";
        case Ops.ReserveStatic:
            return "ReserveStatic";
        case Ops.LoadStatic:
            return "LoadStatic";
        case Ops.StoreStatic:
            return "StoreStatic";
        case Ops.LoadLocal:
            return "LoadLocal";
        case Ops.StoreLocal:
            return "StoreLocal";
        case Ops.Call:
            return "Call";
        case Ops.Return:
            return "Return";
        case Ops.Jump:
            return "Jump";
        case Ops.JumpIfTrue:
            return "JumpIfTrue";
        case Ops.Builtin:
            return "Builtin";
        case Ops.Add:
            return "Add";
        case Ops.Subtract:
            return "Subtract";
        case Ops.Multiply:
            return "Multiply";
        case Ops.Divide:
            return "Divide";
        case Ops.Remainder:
            return "Remainder";
        case Ops.Equal:
            return "Equal";
        case Ops.LessThan:
            return "LessThan";
        case Ops.And:
            return "And";
        case Ops.Or:
            return "Or";
        case Ops.Xor:
            return "Xor";
        case Ops.Not:
            return "Not";
        case Ops.SourceMap:
            return "SourceMap";
        default:
            return `<unknown Op ${op}>`;
    }
}

export function builtinToString(builtin: number): string {
    switch (builtin) {
        case Builtins.StringConcat:
            return "StringConcat";
        case Builtins.StringEqual:
            return "StringEqual";
        case Builtins.ArraySet:
            return "ArraySet";
        case Builtins.StructSet:
            return "StructSet";
        case Builtins.Print:
            return "Print";
        default:
            return `<unknown Builtin ${builtin}>`;
    }
}