use anyhow::{bail, Result};

use crate::compiler;

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Instruction {
    Nop,
    Debug,
    Quit,
    Push(i32),
    Drop,
    Depth,
    Pick,
    Dup,
    Swap,
    Over,
    Rot,
    Nip,
    I,
    J,
    Tuck,
    Fetch,
    FetchPrint,
    Store,
    PlusStore,
    MinusStore,
    Cells,
    Allot,
    Dot,
    Emit,
    Space,
    Spaces,
    Cr,
    StringPrint(usize),
    StringReference(usize),
    Count,
    Not,
    And,
    Nand,
    Or,
    Nor,
    Xor,
    Xnor,
    Lsh,
    Rsh,
    Compare,
    Eq,
    Neq,
    Lt,
    Leq,
    Gt,
    Geq,
    Min,
    Max,
    Boolean,
    Invert,
    Random,
    Plus,
    OnePlus,
    TwoPlus,
    Minus,
    OneMinus,
    TwoMinus,
    Times,
    Divide,
    Mod,
    AsmQuote(String),

    Multiple {
        instruction: Box<Self>,
        count: usize,
    },

    Call(String),
    Condition(usize),
}

impl compiler::Compilable<compiler::Compiler, hence::parser::ast::Body> for Instruction {
    fn compile(&self, compiler: &compiler::Compiler) -> Result<hence::parser::ast::Body> {
        match self {
            Instruction::Nop => Ok(vec![hence::parser::ast::Node::Call {
                name: "nop".to_string(),
                arg: None,
            }]),
            Instruction::Debug => Ok(vec![hence::parser::ast::Node::Call {
                name: "dbg".to_string(),
                arg: None,
            }]),
            Instruction::Quit => Ok(vec![hence::parser::ast::Node::MacroCall {
                name: "std_stop".to_string(),
                args: vec![],
            }]),
            Instruction::Push(x) => Ok(vec![hence::parser::ast::Node::Call {
                name: "push".to_string(),
                arg: Some(hence::arg::Arg::Number(*x as u16)),
            }]),
            Instruction::Drop => Ok(vec![hence::parser::ast::Node::Call {
                name: "pop".to_string(),
                arg: None,
            }]),
            Instruction::Depth => Ok(vec![
                hence::parser::ast::Node::Call {
                    name: "tsr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_SP".to_string())),
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
            ]),
            Instruction::Pick => Ok(vec![
                hence::parser::ast::Node::Call {
                    name: "tsr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_SP".to_string())),
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())),
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_ld".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())),
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_alu".to_string(),
                    args: vec![hence::arg::Arg::Variable("CORE_ALU_SUB".to_string())],
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())),
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_rset".to_string(),
                    args: vec![
                        hence::arg::Arg::Variable("CORE_REG_B".to_string()),
                        hence::arg::Arg::BinaryExpression {
                            left: Box::new(hence::arg::Arg::Variable(
                                "CORE_MEM_PRG_END".to_string(),
                            )),
                            right: Box::new(hence::arg::Arg::Number(2)),
                            op: hence::arg::BinaryExpressionOperator::Sub,
                        },
                    ],
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_alu".to_string(),
                    args: vec![hence::arg::Arg::Variable("CORE_ALU_ADD".to_string())],
                },
                hence::parser::ast::Node::Call {
                    name: "get".to_string(),
                    arg: None,
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
            ]),
            Instruction::Dup => Ok(vec![
                hence::parser::ast::Node::Call {
                    name: "tss".to_string(),
                    arg: None,
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
            ]),
            Instruction::Swap => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "std_ld".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())),
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_ld".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())),
                },
                hence::parser::ast::Node::Call {
                    name: "tsr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())),
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
                hence::parser::ast::Node::Call {
                    name: "tsr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())),
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
            ]),
            Instruction::Over => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "std_ld".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())),
                },
                hence::parser::ast::Node::Call {
                    name: "tss".to_string(),
                    arg: None,
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())),
                },
                hence::parser::ast::Node::Call {
                    name: "tsr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())),
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
                hence::parser::ast::Node::Call {
                    name: "tsr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())),
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
            ]),
            Instruction::Rot => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "std_ld".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())),
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_ld".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())),
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_ld".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_C".to_string())),
                },
                hence::parser::ast::Node::Call {
                    name: "tsr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())),
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
                hence::parser::ast::Node::Call {
                    name: "tsr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())),
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
                hence::parser::ast::Node::Call {
                    name: "tsr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_C".to_string())),
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
            ]),
            Instruction::Nip => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "std_ld".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::Call {
                    name: "pop".to_string(),
                    arg: None,
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
            ]),
            Instruction::I => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "std_get".to_string(),
                    args: vec![hence::arg::Arg::Variable("MEM_LOOP_I".to_string())],
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
            ]),
            Instruction::J => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "std_get".to_string(),
                    args: vec![hence::arg::Arg::Variable("MEM_LOOP_J".to_string())],
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
            ]),
            Instruction::Tuck => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "std_ld".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())),
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_ld".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())),
                },
                hence::parser::ast::Node::Call {
                    name: "tsr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())),
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
                hence::parser::ast::Node::Call {
                    name: "tsr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())),
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
                hence::parser::ast::Node::Call {
                    name: "tsr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())),
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
            ]),
            Instruction::Fetch => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "std_ld".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::Call {
                    name: "get".to_string(),
                    arg: None,
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
            ]),
            Instruction::FetchPrint => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "std_ld".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::Call {
                    name: "get".to_string(),
                    arg: None,
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())),
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_set".to_string(),
                    args: vec![hence::arg::Arg::Variable("CORE_MEM_OUT".to_string())],
                },
            ]),
            Instruction::Store => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "std_ld".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())),
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_ld".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())),
                },
                hence::parser::ast::Node::Call {
                    name: "tsr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())),
                },
                hence::parser::ast::Node::Call {
                    name: "set".to_string(),
                    arg: None,
                },
            ]),
            Instruction::PlusStore => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "std_ld".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_C".to_string())),
                },
                hence::parser::ast::Node::Call {
                    name: "get".to_string(),
                    arg: None,
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())),
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_ld".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())),
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_alu".to_string(),
                    args: vec![hence::arg::Arg::Variable("CORE_ALU_ADD".to_string())],
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())),
                },
                hence::parser::ast::Node::Call {
                    name: "tsr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_C".to_string())),
                },
                hence::parser::ast::Node::Call {
                    name: "set".to_string(),
                    arg: None,
                },
            ]),
            Instruction::MinusStore => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "std_ld".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_C".to_string())),
                },
                hence::parser::ast::Node::Call {
                    name: "get".to_string(),
                    arg: None,
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())),
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_ld".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())),
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_alu".to_string(),
                    args: vec![hence::arg::Arg::Variable("CORE_ALU_SUB".to_string())],
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())),
                },
                hence::parser::ast::Node::Call {
                    name: "tsr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_C".to_string())),
                },
                hence::parser::ast::Node::Call {
                    name: "set".to_string(),
                    arg: None,
                },
            ]),
            Instruction::Cells => Ok(vec![]),
            Instruction::Allot => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "std_get".to_string(),
                    args: vec![hence::arg::Arg::Variable("MEM_ALLOC_PTR".to_string())],
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())),
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_ld".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())),
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_alu".to_string(),
                    args: vec![hence::arg::Arg::Variable("CORE_ALU_ADD".to_string())],
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())),
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_set".to_string(),
                    args: vec![hence::arg::Arg::Variable("MEM_ALLOC_PTR".to_string())],
                },
            ]),
            Instruction::Dot => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "std_ld".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())),
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_set".to_string(),
                    args: vec![hence::arg::Arg::Variable("CORE_MEM_OUT".to_string())],
                },
            ]),
            Instruction::Emit => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "std_ld".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())),
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_set".to_string(),
                    args: vec![hence::arg::Arg::Variable("CORE_MEM_CHR".to_string())],
                },
            ]),
            Instruction::Space => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "std_rset".to_string(),
                    args: vec![
                        hence::arg::Arg::Variable("CORE_REG_A".to_string()),
                        hence::arg::Arg::Char(' '),
                    ],
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_set".to_string(),
                    args: vec![hence::arg::Arg::Variable("CORE_MEM_CHR".to_string())],
                },
            ]),
            Instruction::Spaces => Ok(vec![]),
            Instruction::Cr => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "std_rset".to_string(),
                    args: vec![
                        hence::arg::Arg::Variable("CORE_REG_A".to_string()),
                        hence::arg::Arg::Char('\n'),
                    ],
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_set".to_string(),
                    args: vec![hence::arg::Arg::Variable("CORE_MEM_CHR".to_string())],
                },
            ]),
            Instruction::StringPrint(x) => {
                let loop_label = format!("loop_strings_{}", x);
                let data_label = format!("data_strings_{}", x);
                let data_end_label = format!("data_strings_end_{}", x);

                Ok(vec![
                    hence::parser::ast::Node::MacroCall {
                        name: "std_rset".to_string(),
                        args: vec![
                            hence::arg::Arg::Variable("CORE_REG_B".to_string()),
                            hence::arg::Arg::Variable(data_label),
                        ],
                    },
                    hence::parser::ast::Node::Label(loop_label.clone()),
                    hence::parser::ast::Node::Call {
                        name: "tsr".to_string(),
                        arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())),
                    },
                    hence::parser::ast::Node::Call {
                        name: "get".to_string(),
                        arg: None,
                    },
                    hence::parser::ast::Node::Call {
                        name: "tlr".to_string(),
                        arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())),
                    },
                    hence::parser::ast::Node::MacroCall {
                        name: "std_set".to_string(),
                        args: vec![hence::arg::Arg::Variable("CORE_MEM_CHR".to_string())],
                    },
                    hence::parser::ast::Node::MacroCall {
                        name: "std_rset".to_string(),
                        args: vec![
                            hence::arg::Arg::Variable("CORE_REG_A".to_string()),
                            hence::arg::Arg::Number(1),
                        ],
                    },
                    hence::parser::ast::Node::MacroCall {
                        name: "std_alu".to_string(),
                        args: vec![hence::arg::Arg::Variable("CORE_ALU_ADD".to_string())],
                    },
                    hence::parser::ast::Node::Call {
                        name: "tlr".to_string(),
                        arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())),
                    },
                    hence::parser::ast::Node::MacroCall {
                        name: "std_rset".to_string(),
                        args: vec![
                            hence::arg::Arg::Variable("CORE_REG_A".to_string()),
                            hence::arg::Arg::Variable(data_end_label),
                        ],
                    },
                    hence::parser::ast::Node::MacroCall {
                        name: "std_alu".to_string(),
                        args: vec![hence::arg::Arg::Variable("CORE_ALU_GT".to_string())],
                    },
                    hence::parser::ast::Node::Call {
                        name: "tlr".to_string(),
                        arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())),
                    },
                    hence::parser::ast::Node::MacroCall {
                        name: "std_cond_jump".to_string(),
                        args: vec![hence::arg::Arg::Variable(loop_label)],
                    },
                ])
            }
            Instruction::StringReference(x) => Ok(vec![
                hence::parser::ast::Node::Call {
                    name: "push".to_string(),
                    arg: Some(hence::arg::Arg::Variable(format!("data_strings_end_{}", x))),
                },
                hence::parser::ast::Node::Call {
                    name: "push".to_string(),
                    arg: Some(hence::arg::Arg::Variable(format!("data_strings_{}", x))),
                },
            ]),
            Instruction::Count => Ok(vec![]),
            Instruction::Not => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "std_ld".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())),
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_alu".to_string(),
                    args: vec![hence::arg::Arg::Variable("CORE_ALU_NOT".to_string())],
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
            ]),
            Instruction::And => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "stack_transfer_alu".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_alu".to_string(),
                    args: vec![hence::arg::Arg::Variable("CORE_ALU_AND".to_string())],
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
            ]),
            Instruction::Nand => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "stack_transfer_alu".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_alu".to_string(),
                    args: vec![hence::arg::Arg::Variable("CORE_ALU_AND".to_string())],
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())),
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_alu".to_string(),
                    args: vec![hence::arg::Arg::Variable("CORE_ALU_NOT".to_string())],
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
            ]),
            Instruction::Or => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "stack_transfer_alu".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_alu".to_string(),
                    args: vec![hence::arg::Arg::Variable("CORE_ALU_OR".to_string())],
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
            ]),
            Instruction::Nor => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "stack_transfer_alu".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_alu".to_string(),
                    args: vec![hence::arg::Arg::Variable("CORE_ALU_OR".to_string())],
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())),
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_alu".to_string(),
                    args: vec![hence::arg::Arg::Variable("CORE_ALU_NOT".to_string())],
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
            ]),
            Instruction::Xor => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "stack_transfer_alu".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_alu".to_string(),
                    args: vec![hence::arg::Arg::Variable("CORE_ALU_XOR".to_string())],
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
            ]),
            Instruction::Xnor => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "stack_transfer_alu".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_alu".to_string(),
                    args: vec![hence::arg::Arg::Variable("CORE_ALU_XOR".to_string())],
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())),
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_alu".to_string(),
                    args: vec![hence::arg::Arg::Variable("CORE_ALU_NOT".to_string())],
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
            ]),
            Instruction::Lsh => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "stack_transfer_alu".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_alu".to_string(),
                    args: vec![hence::arg::Arg::Variable("CORE_ALU_LSH".to_string())],
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
            ]),
            Instruction::Rsh => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "stack_transfer_alu".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_alu".to_string(),
                    args: vec![hence::arg::Arg::Variable("CORE_ALU_RSH".to_string())],
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
            ]),
            Instruction::Compare => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "stack_transfer_alu".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_alu".to_string(),
                    args: vec![hence::arg::Arg::Variable("CORE_ALU_CMP".to_string())],
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
            ]),
            Instruction::Eq => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "stack_transfer_alu".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_alu".to_string(),
                    args: vec![hence::arg::Arg::Variable("CORE_ALU_EQ".to_string())],
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
            ]),
            Instruction::Neq => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "stack_transfer_alu".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_alu".to_string(),
                    args: vec![hence::arg::Arg::Variable("CORE_ALU_EQ".to_string())],
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())),
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_alu".to_string(),
                    args: vec![hence::arg::Arg::Variable("CORE_ALU_INV".to_string())],
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
            ]),
            Instruction::Lt => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "stack_transfer_alu".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_alu".to_string(),
                    args: vec![hence::arg::Arg::Variable("CORE_ALU_LT".to_string())],
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
            ]),
            Instruction::Leq => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "stack_transfer_alu".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_alu".to_string(),
                    args: vec![hence::arg::Arg::Variable("CORE_ALU_LEQ".to_string())],
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
            ]),
            Instruction::Gt => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "stack_transfer_alu".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_alu".to_string(),
                    args: vec![hence::arg::Arg::Variable("CORE_ALU_GT".to_string())],
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
            ]),
            Instruction::Geq => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "stack_transfer_alu".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_alu".to_string(),
                    args: vec![hence::arg::Arg::Variable("CORE_ALU_GEQ".to_string())],
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
            ]),
            Instruction::Min => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "std_ld".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_C".to_string())),
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())),
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_ld".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())),
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_alu".to_string(),
                    args: vec![hence::arg::Arg::Variable("CORE_ALU_GT".to_string())],
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())),
                },
                hence::parser::ast::Node::Call {
                    name: "tsr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())),
                },
                hence::parser::ast::Node::Call {
                    name: "tlrc".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_C".to_string())),
                },
                hence::parser::ast::Node::Call {
                    name: "tsr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_C".to_string())),
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
            ]),
            Instruction::Max => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "std_ld".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_C".to_string())),
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())),
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_ld".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())),
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_alu".to_string(),
                    args: vec![hence::arg::Arg::Variable("CORE_ALU_LT".to_string())],
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())),
                },
                hence::parser::ast::Node::Call {
                    name: "tsr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())),
                },
                hence::parser::ast::Node::Call {
                    name: "tlrc".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_C".to_string())),
                },
                hence::parser::ast::Node::Call {
                    name: "tsr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_C".to_string())),
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
            ]),
            Instruction::Boolean => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "std_ld".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())),
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_alu".to_string(),
                    args: vec![hence::arg::Arg::Variable("CORE_ALU_BOL".to_string())],
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
            ]),
            Instruction::Invert => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "std_ld".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())),
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_alu".to_string(),
                    args: vec![hence::arg::Arg::Variable("CORE_ALU_INV".to_string())],
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
            ]),
            Instruction::Random => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "std_alu".to_string(),
                    args: vec![hence::arg::Arg::Variable("CORE_ALU_RND".to_string())],
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
            ]),
            Instruction::Plus => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "stack_transfer_alu".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_alu".to_string(),
                    args: vec![hence::arg::Arg::Variable("CORE_ALU_ADD".to_string())],
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
            ]),
            Instruction::OnePlus => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "std_ld".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())),
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_rset".to_string(),
                    args: vec![
                        hence::arg::Arg::Variable("CORE_REG_B".to_string()),
                        hence::arg::Arg::Number(1),
                    ],
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_alu".to_string(),
                    args: vec![hence::arg::Arg::Variable("CORE_ALU_ADD".to_string())],
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
            ]),
            Instruction::TwoPlus => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "std_ld".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())),
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_rset".to_string(),
                    args: vec![
                        hence::arg::Arg::Variable("CORE_REG_B".to_string()),
                        hence::arg::Arg::Number(2),
                    ],
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_alu".to_string(),
                    args: vec![hence::arg::Arg::Variable("CORE_ALU_ADD".to_string())],
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
            ]),
            Instruction::Minus => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "stack_transfer_alu".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_alu".to_string(),
                    args: vec![hence::arg::Arg::Variable("CORE_ALU_SUB".to_string())],
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
            ]),
            Instruction::OneMinus => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "std_ld".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())),
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_rset".to_string(),
                    args: vec![
                        hence::arg::Arg::Variable("CORE_REG_B".to_string()),
                        hence::arg::Arg::Number(1),
                    ],
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_alu".to_string(),
                    args: vec![hence::arg::Arg::Variable("CORE_ALU_SUB".to_string())],
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
            ]),
            Instruction::TwoMinus => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "std_ld".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::Call {
                    name: "tlr".to_string(),
                    arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())),
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_rset".to_string(),
                    args: vec![
                        hence::arg::Arg::Variable("CORE_REG_B".to_string()),
                        hence::arg::Arg::Number(2),
                    ],
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_alu".to_string(),
                    args: vec![hence::arg::Arg::Variable("CORE_ALU_SUB".to_string())],
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
            ]),
            Instruction::Times => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "stack_transfer_alu".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_alu".to_string(),
                    args: vec![hence::arg::Arg::Variable("CORE_ALU_MUL".to_string())],
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
            ]),
            Instruction::Divide => Ok(vec![
                hence::parser::ast::Node::MacroCall {
                    name: "stack_transfer_alu".to_string(),
                    args: vec![],
                },
                hence::parser::ast::Node::MacroCall {
                    name: "std_alu".to_string(),
                    args: vec![hence::arg::Arg::Variable("CORE_ALU_DIV".to_string())],
                },
                hence::parser::ast::Node::Call {
                    name: "tls".to_string(),
                    arg: None,
                },
            ]),
            Instruction::Mod => Ok(vec![]),
            Instruction::Call(x) => match compiler.words.get(x) {
                Some(w) => {
                    if w.times_used > 1 {
                        Ok(vec![hence::parser::ast::Node::MacroCall {
                            name: "call_stack_jump".to_string(),
                            args: vec![hence::arg::Arg::Variable(format!("words_{}", w.id))],
                        }])
                    } else {
                        Ok(w.instructions
                            .iter()
                            .map(|ins| ins.compile(compiler))
                            .collect::<Result<Vec<hence::parser::ast::Body>>>()?
                            .into_iter()
                            .flatten()
                            .collect())
                    }
                }
                None => bail!("Unknown word: {}", x),
            },
            // Instruction::Call(x) => bail!("Unknown word: {}", x),
            Instruction::AsmQuote(x) => Ok(hence::parser::parse(hence::lexer::lex(x)?)?.body),
            // Instruction::Condition(x) => Ok(vec![]),
            Instruction::Condition(x) => match compiler.conditions.get(*x) {
                Some(cond) => Ok(vec![if cond.only_if() {
                    hence::parser::ast::Node::MacroCall {
                        name: "call_stack_jump_cond_if".to_string(),
                        args: vec![hence::arg::Arg::Variable(format!("conditions_if_{}", x))],
                    }
                } else {
                    hence::parser::ast::Node::MacroCall {
                        name: "call_stack_jump_cond_if_else".to_string(),
                        args: vec![
                            hence::arg::Arg::Variable(format!("conditions_if_{}", x)),
                            hence::arg::Arg::Variable(format!("conditions_else_{}", x)),
                        ],
                    }
                }]),
                None => bail!("Condition by index not found: {}", x),
            },

            Instruction::Multiple { instruction, count } => {
                if *count == 0 {
                    Ok(vec![])
                } else if *count == 1 {
                    Ok(instruction.compile(compiler)?)
                } else {
                    match **instruction {
                        Instruction::Nop => Ok(Instruction::Nop.compile(compiler)?),
                        Instruction::Quit => Ok(Instruction::Quit.compile(compiler)?),
                        Instruction::Push(x) => Ok([
                            Instruction::Push(x).compile(compiler)?,
                            [hence::parser::ast::Node::Call {
                                name: "tls".to_string(),
                                arg: None,
                            }]
                            .into_iter()
                            .cycle()
                            .take(count - 1)
                            .collect(),
                        ]
                        .concat()),
                        Instruction::Depth => Ok([
                            Instruction::Depth.compile(compiler)?,
                            Instruction::Multiple {
                                instruction: Box::new(Instruction::Dup),
                                count: count - 1,
                            }
                            .compile(compiler)?,
                        ]
                        .concat()),
                        Instruction::Dup => Ok([
                            Instruction::Dup.compile(compiler)?,
                            [hence::parser::ast::Node::Call {
                                name: "tls".to_string(),
                                arg: None,
                            }]
                            .into_iter()
                            .cycle()
                            .take(count - 1)
                            .collect(),
                        ]
                        .concat()),
                        Instruction::Swap => {
                            if count % 2 == 0 {
                                Ok(vec![])
                            } else {
                                Ok(Instruction::Swap.compile(compiler)?)
                            }
                        }
                        Instruction::Over => Ok([
                            Instruction::Over.compile(compiler)?,
                            (0..count - 1)
                                .flat_map(|n| {
                                    if n % 2 == 0 {
                                        vec![
                                            hence::parser::ast::Node::Call {
                                                name: "tsr".to_string(),
                                                arg: Some(hence::arg::Arg::Variable(
                                                    "CORE_REG_A".to_string(),
                                                )),
                                            },
                                            hence::parser::ast::Node::Call {
                                                name: "tls".to_string(),
                                                arg: None,
                                            },
                                        ]
                                    } else {
                                        vec![
                                            hence::parser::ast::Node::Call {
                                                name: "tsr".to_string(),
                                                arg: Some(hence::arg::Arg::Variable(
                                                    "CORE_REG_B".to_string(),
                                                )),
                                            },
                                            hence::parser::ast::Node::Call {
                                                name: "tls".to_string(),
                                                arg: None,
                                            },
                                        ]
                                    }
                                })
                                .collect(),
                        ]
                        .concat()),
                        Instruction::Rot => match count % 3 {
                            0 => Ok(vec![]),
                            1 => Ok(Instruction::Rot.compile(compiler)?),
                            2 => Ok(vec![
                                hence::parser::ast::Node::MacroCall {
                                    name: "std_ld".to_string(),
                                    args: vec![],
                                },
                                hence::parser::ast::Node::Call {
                                    name: "tlr".to_string(),
                                    arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())),
                                },
                                hence::parser::ast::Node::MacroCall {
                                    name: "std_ld".to_string(),
                                    args: vec![],
                                },
                                hence::parser::ast::Node::Call {
                                    name: "tlr".to_string(),
                                    arg: Some(hence::arg::Arg::Variable("CORE_REG_C".to_string())),
                                },
                                hence::parser::ast::Node::MacroCall {
                                    name: "std_ld".to_string(),
                                    args: vec![],
                                },
                                hence::parser::ast::Node::Call {
                                    name: "tlr".to_string(),
                                    arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())),
                                },
                                hence::parser::ast::Node::Call {
                                    name: "tsr".to_string(),
                                    arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())),
                                },
                                hence::parser::ast::Node::Call {
                                    name: "tls".to_string(),
                                    arg: None,
                                },
                                hence::parser::ast::Node::Call {
                                    name: "tsr".to_string(),
                                    arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())),
                                },
                                hence::parser::ast::Node::Call {
                                    name: "tls".to_string(),
                                    arg: None,
                                },
                                hence::parser::ast::Node::Call {
                                    name: "tsr".to_string(),
                                    arg: Some(hence::arg::Arg::Variable("CORE_REG_C".to_string())),
                                },
                                hence::parser::ast::Node::Call {
                                    name: "tls".to_string(),
                                    arg: None,
                                },
                            ]),
                            x => bail!("Remainder of count % 3 cannot be higher than 3: {}", x),
                        },
                        Instruction::Nip => Ok([
                            vec![hence::parser::ast::Node::MacroCall {
                                name: "std_ld".to_string(),
                                args: vec![],
                            }],
                            [hence::parser::ast::Node::Call {
                                name: "pop".to_string(),
                                arg: None,
                            }]
                            .into_iter()
                            .cycle()
                            .take(*count)
                            .collect(),
                            vec![hence::parser::ast::Node::Call {
                                name: "tls".to_string(),
                                arg: None,
                            }],
                        ]
                        .concat()),
                        Instruction::I | Instruction::J => Ok([
                            instruction.compile(compiler)?,
                            [hence::parser::ast::Node::Call {
                                name: "tls".to_string(),
                                arg: None,
                            }]
                            .into_iter()
                            .cycle()
                            .take(count - 1)
                            .collect(),
                        ]
                        .concat()),
                        Instruction::Fetch => Ok([
                            vec![hence::parser::ast::Node::MacroCall {
                                name: "std_ld".to_string(),
                                args: vec![],
                            }],
                            [hence::parser::ast::Node::Call {
                                name: "get".to_string(),
                                arg: None,
                            }]
                            .into_iter()
                            .cycle()
                            .take(*count)
                            .collect(),
                            vec![hence::parser::ast::Node::Call {
                                name: "tls".to_string(),
                                arg: None,
                            }],
                        ]
                        .concat()),
                        Instruction::Space | Instruction::Cr => Ok([
                            instruction.compile(compiler)?,
                            [hence::parser::ast::Node::Call {
                                name: "set".to_string(),
                                arg: None,
                            }]
                            .into_iter()
                            .cycle()
                            .take(count - 1)
                            .collect(),
                        ]
                        .concat()),
                        _ => {
                            let compiled = instruction.compile(compiler)?;
                            let len = compiled.len() * count;
                            Ok(compiled.into_iter().cycle().take(len).collect())
                        }
                    }
                }
            }
        }
    }
}

impl Instruction {
    pub fn from_word(word: &str) -> Option<Self> {
        match word.to_lowercase().as_str() {
            "nop" => Some(Instruction::Nop),
            "debug" => Some(Instruction::Debug),
            "quit" => Some(Instruction::Quit),
            "drop" => Some(Instruction::Drop),
            "depth" => Some(Instruction::Depth),
            "pick" => Some(Instruction::Pick),
            "dup" => Some(Instruction::Dup),
            "swap" => Some(Instruction::Swap),
            "over" => Some(Instruction::Over),
            "rot" => Some(Instruction::Rot),
            "nip" => Some(Instruction::Nip),
            "i" => Some(Instruction::I),
            "j" => Some(Instruction::J),
            "tuck" => Some(Instruction::Tuck),
            "@" => Some(Instruction::Fetch),
            "?" => Some(Instruction::FetchPrint),
            "!" => Some(Instruction::Store),
            "+!" => Some(Instruction::PlusStore),
            "-!" => Some(Instruction::MinusStore),
            "cells" => Some(Instruction::Cells),
            "allot" => Some(Instruction::Allot),
            "." => Some(Instruction::Dot),
            "emit" => Some(Instruction::Emit),
            "space" => Some(Instruction::Space),
            "spaces" => Some(Instruction::Spaces),
            "cr" => Some(Instruction::Cr),
            "count" => Some(Instruction::Count),
            "not" => Some(Instruction::Not),
            "and" => Some(Instruction::And),
            "nand" => Some(Instruction::Nand),
            "or" => Some(Instruction::Or),
            "nor" => Some(Instruction::Nor),
            "xor" => Some(Instruction::Xor),
            "xnor" => Some(Instruction::Xnor),
            "lshift" => Some(Instruction::Lsh),
            "rshift" => Some(Instruction::Rsh),
            "compare" => Some(Instruction::Compare),
            "=" => Some(Instruction::Eq),
            "!=" => Some(Instruction::Neq),
            "<" => Some(Instruction::Lt),
            "<=" => Some(Instruction::Leq),
            ">" => Some(Instruction::Gt),
            ">=" => Some(Instruction::Geq),
            "min" => Some(Instruction::Min),
            "max" => Some(Instruction::Max),
            "boolean" => Some(Instruction::Boolean),
            "invert" => Some(Instruction::Invert),
            "random" => Some(Instruction::Random),
            "+" => Some(Instruction::Plus),
            "1+" => Some(Instruction::OnePlus),
            "2+" => Some(Instruction::TwoPlus),
            "-" => Some(Instruction::Minus),
            "1-" => Some(Instruction::OneMinus),
            "2-" => Some(Instruction::TwoMinus),
            "*" => Some(Instruction::Times),
            "/" => Some(Instruction::Divide),
            "mod" => Some(Instruction::Mod),
            _ => None,
        }
    }
}
