#![allow(non_upper_case_globals)] use super::*; use crate::lexer::token; /// Because [assert_matches](core::assert_matches::assert_matches) is unstable macro_rules! assert_matches { ($e: expr, $($p: pat $(if $condition:expr)?)* ) => { match $e { $($p $(if $condition)? => (),)* _ => panic!("{}", stringify!($e did not match $($p),*)), } }; } /// Simplified grammar for constructing an expression macro_rules! expr { ($ident:ident) => { Expr::Ident(stringify!($ident)).into() }; ($lit:literal) => { Expr::Number($lit).into() }; (& $lit:literal) => { Expr::AddrOf($lit).into() }; (($($t:tt)*)) => { Expr::Group(expr!($($t)*)) }; ([$($op:tt)*] $($t:tt)*) => { Expr::Unary(vec![$(UnOp::$op),*], expr!($($t)*)) }; (($($a:tt)*) $($op:tt ($($b:tt)*))+) => { Expr::Binary(expr!($($a)*), vec![$((BinOp::$op, expr!($($b)*))),+]) } } macro_rules! passert { ($expected:expr, $text:literal) => { assert_eq!($expected, Parsable::parse($text).unwrap()) }; } // #[test] // fn statements() { // passert!(, ""); // } // #[test] // fn statement() { // passert!(, ""); // } #[test] fn directive() { passert!(Directive::Org(expr!(0x8000)), ".org 0x8000"); passert!(Directive::String("Hello, world!"), ".string \"Hello, world!\""); assert_eq!(Directive::parse(".word 0x40").unwrap(), Directive::Word(expr!(0x40))); passert!( Directive::Words(vec![expr!(0x40), expr!(0x41), expr!(0x42), expr!(0x43)]), ".words [ 0x40 0x41 0x42 0x43 ]" ); } // #[test] // fn instruction() { // passert!(, ""); // } #[test] fn instruction_kind() { assert_matches!(Parsable::parse("nop").unwrap(), InstructionKind::NoEm(NoEm { .. })); assert_matches!(Parsable::parse("pop sp").unwrap(), InstructionKind::OneEm(OneEm { .. })); } #[test] fn no_em() { passert!(NoEm { opcode: token::NoEm::Nop }, "nop"); passert!(NoEm { opcode: token::NoEm::Ret }, "ret"); passert!(NoEm { opcode: token::NoEm::Clrc }, "clrc"); passert!(NoEm { opcode: token::NoEm::Clrz }, "clrz"); passert!(NoEm { opcode: token::NoEm::Clrn }, "clrn"); passert!(NoEm { opcode: token::NoEm::Setc }, "setc"); passert!(NoEm { opcode: token::NoEm::Setz }, "setz"); passert!(NoEm { opcode: token::NoEm::Setn }, "setn"); passert!(NoEm { opcode: token::NoEm::Dint }, "dint"); passert!(NoEm { opcode: token::NoEm::Eint }, "eint"); } #[test] fn one_em() { const dst: Dst = Dst::Direct(Reg::R15); let width = Width::Word; passert!(OneEm { opcode: token::OneEm::Pop, width, dst }, "pop r15"); passert!(OneEm { opcode: token::OneEm::Rla, width, dst }, "rla r15"); passert!(OneEm { opcode: token::OneEm::Rlc, width, dst }, "rlc r15"); passert!(OneEm { opcode: token::OneEm::Inv, width, dst }, "inv r15"); passert!(OneEm { opcode: token::OneEm::Clr, width, dst }, "clr r15"); passert!(OneEm { opcode: token::OneEm::Tst, width, dst }, "tst r15"); passert!(OneEm { opcode: token::OneEm::Dec, width, dst }, "dec r15"); passert!(OneEm { opcode: token::OneEm::Decd, width, dst }, "decd r15"); passert!(OneEm { opcode: token::OneEm::Inc, width, dst }, "inc r15"); passert!(OneEm { opcode: token::OneEm::Incd, width, dst }, "incd r15"); passert!(OneEm { opcode: token::OneEm::Adc, width, dst }, "adc r15"); passert!(OneEm { opcode: token::OneEm::Dadc, width, dst }, "dadc r15"); passert!(OneEm { opcode: token::OneEm::Sbc, width, dst }, "sbc r15"); let width = Width::Byte; passert!(OneEm { opcode: token::OneEm::Pop, width, dst }, "pop.b r15"); passert!(OneEm { opcode: token::OneEm::Rla, width, dst }, "rla.b r15"); passert!(OneEm { opcode: token::OneEm::Rlc, width, dst }, "rlc.b r15"); passert!(OneEm { opcode: token::OneEm::Inv, width, dst }, "inv.b r15"); passert!(OneEm { opcode: token::OneEm::Clr, width, dst }, "clr.b r15"); passert!(OneEm { opcode: token::OneEm::Tst, width, dst }, "tst.b r15"); passert!(OneEm { opcode: token::OneEm::Dec, width, dst }, "dec.b r15"); passert!(OneEm { opcode: token::OneEm::Decd, width, dst }, "decd.b r15"); passert!(OneEm { opcode: token::OneEm::Inc, width, dst }, "inc.b r15"); passert!(OneEm { opcode: token::OneEm::Incd, width, dst }, "incd.b r15"); passert!(OneEm { opcode: token::OneEm::Adc, width, dst }, "adc.b r15"); passert!(OneEm { opcode: token::OneEm::Dadc, width, dst }, "dadc.b r15"); passert!(OneEm { opcode: token::OneEm::Sbc, width, dst }, "sbc.b r15"); } #[test] fn one_arg() { const src: Src = Src::Direct(Reg::PC); let width = Width::Word; passert!(OneArg { opcode: token::OneArg::Rrc, width, src }, "rrc pc"); passert!(OneArg { opcode: token::OneArg::Swpb, width, src }, "swpb pc"); passert!(OneArg { opcode: token::OneArg::Rra, width, src }, "rra pc"); passert!(OneArg { opcode: token::OneArg::Sxt, width, src }, "sxt pc"); passert!(OneArg { opcode: token::OneArg::Push, width, src }, "push pc"); passert!(OneArg { opcode: token::OneArg::Call, width, src }, "call pc"); let width = Width::Byte; passert!(OneArg { opcode: token::OneArg::Rrc, width, src }, "rrc.b pc"); passert!(OneArg { opcode: token::OneArg::Swpb, width, src }, "swpb.b pc"); passert!(OneArg { opcode: token::OneArg::Rra, width, src }, "rra.b pc"); passert!(OneArg { opcode: token::OneArg::Sxt, width, src }, "sxt.b pc"); passert!(OneArg { opcode: token::OneArg::Push, width, src }, "push.b pc"); passert!(OneArg { opcode: token::OneArg::Call, width, src }, "call.b pc"); } #[test] fn two_arg() { const src: Src = Src::Direct(Reg::R14); const dst: Dst = Dst::Direct(Reg::R15); let width = Width::Word; passert!(TwoArg { opcode: token::TwoArg::Mov, width, src, dst }, "mov r14, r15"); passert!(TwoArg { opcode: token::TwoArg::Add, width, src, dst }, "add r14, r15"); passert!(TwoArg { opcode: token::TwoArg::Addc, width, src, dst }, "addc r14, r15"); passert!(TwoArg { opcode: token::TwoArg::Subc, width, src, dst }, "subc r14, r15"); passert!(TwoArg { opcode: token::TwoArg::Sub, width, src, dst }, "sub r14, r15"); passert!(TwoArg { opcode: token::TwoArg::Cmp, width, src, dst }, "cmp r14, r15"); passert!(TwoArg { opcode: token::TwoArg::Dadd, width, src, dst }, "dadd r14, r15"); passert!(TwoArg { opcode: token::TwoArg::Bit, width, src, dst }, "bit r14, r15"); passert!(TwoArg { opcode: token::TwoArg::Bic, width, src, dst }, "bic r14, r15"); passert!(TwoArg { opcode: token::TwoArg::Bis, width, src, dst }, "bis r14, r15"); passert!(TwoArg { opcode: token::TwoArg::Xor, width, src, dst }, "xor r14, r15"); passert!(TwoArg { opcode: token::TwoArg::And, width, src, dst }, "and r14, r15"); let width = Width::Byte; passert!(TwoArg { opcode: token::TwoArg::Mov, width, src, dst }, "mov.b r14, r15"); passert!(TwoArg { opcode: token::TwoArg::Add, width, src, dst }, "add.b r14, r15"); passert!(TwoArg { opcode: token::TwoArg::Addc, width, src, dst }, "addc.b r14, r15"); passert!(TwoArg { opcode: token::TwoArg::Subc, width, src, dst }, "subc.b r14, r15"); passert!(TwoArg { opcode: token::TwoArg::Sub, width, src, dst }, "sub.b r14, r15"); passert!(TwoArg { opcode: token::TwoArg::Cmp, width, src, dst }, "cmp.b r14, r15"); passert!(TwoArg { opcode: token::TwoArg::Dadd, width, src, dst }, "dadd.b r14, r15"); passert!(TwoArg { opcode: token::TwoArg::Bit, width, src, dst }, "bit.b r14, r15"); passert!(TwoArg { opcode: token::TwoArg::Bic, width, src, dst }, "bic.b r14, r15"); passert!(TwoArg { opcode: token::TwoArg::Bis, width, src, dst }, "bis.b r14, r15"); passert!(TwoArg { opcode: token::TwoArg::Xor, width, src, dst }, "xor.b r14, r15"); passert!(TwoArg { opcode: token::TwoArg::And, width, src, dst }, "and.b r14, r15"); } #[test] fn jump() { const dst100: JumpDst = JumpDst::Rel(100); passert!(Jump { opcode: token::Jump::Jne, dst: dst100 }, "jne 100"); passert!(Jump { opcode: token::Jump::Jnz, dst: dst100 }, "jnz 100"); passert!(Jump { opcode: token::Jump::Jeq, dst: dst100 }, "jeq 100"); passert!(Jump { opcode: token::Jump::Jz, dst: dst100 }, "jz 100"); passert!(Jump { opcode: token::Jump::Jnc, dst: dst100 }, "jnc 100"); passert!(Jump { opcode: token::Jump::Jlo, dst: dst100 }, "jlo 100"); passert!(Jump { opcode: token::Jump::Jc, dst: dst100 }, "jc 100"); passert!(Jump { opcode: token::Jump::Jhs, dst: dst100 }, "jhs 100"); passert!(Jump { opcode: token::Jump::Jn, dst: dst100 }, "jn 100"); passert!(Jump { opcode: token::Jump::Jge, dst: dst100 }, "jge 100"); passert!(Jump { opcode: token::Jump::Jl, dst: dst100 }, "jl 100"); passert!(Jump { opcode: token::Jump::Jmp, dst: dst100 }, "jmp 100"); } #[test] fn reti() { passert!(Reti, "reti"); } #[test] fn br() { passert!(Br { src: Src::Direct(Reg::R15) }, "br r15"); } #[test] fn width() { passert!(Width::Byte, ".b"); passert!(Width::Word, ".w"); passert!(Width::Word, ""); } #[test] fn src() { passert!(Src::Direct(Reg::R15), "r15"); passert!(Src::Indexed(expr!(0x1000), Reg::R15), "0x1000(r15)"); passert!(Src::Indirect(Reg::R15), "@r15"); passert!(Src::PostInc(Reg::R15), "@r15+"); passert!(Src::Absolute(expr!(0x1000)), "&0x1000"); passert!(Src::Immediate(expr!(0x1000)), "#0x1000"); passert!(Src::BareExpr(expr!(foo)), "foo"); } #[test] fn dst() { passert!(Dst::Direct(Reg::R15), "r15"); passert!(Dst::Indexed(expr!(0x1000), Reg::R15), "0x1000(r15)"); passert!(Dst::Absolute(expr!(0x1000)), "&0x1000"); passert!(Dst::Special(DstSpecial::Zero), "#0"); passert!(Dst::Special(DstSpecial::One), "#1"); } #[test] fn jump_dst() { passert!(JumpDst::Rel(100), "100"); passert!(JumpDst::Rel(-100), "-100"); passert!(JumpDst::Label("foo"), "foo"); } #[test] fn expr() { // Terms= passert!(expr!((1) Mul(2) Rem(3) Div(4)), "1 * 2 % 3 / 4"); // Factors passert!(expr!((1) Add(2) Sub(3)), "1 + 2 - 3"); // Shift passert!(expr!((1) Lsh(2) Rsh(3)), "1 << 2 >> 3"); // Bitwise logic passert!(expr!((1) And(2) Or(3) Xor(4)), "1 & 2 | 3 ^ 4"); // Unary passert!(expr!([Deref Neg Not] 1), "*-!1"); // Number passert!(Expr::Number(42), "42"); // Identifier passert!(Expr::Ident("foo"), "foo"); // Addrof passert!(Expr::AddrOf("bar"), "&bar"); // Group passert!(expr!((42)), "(42)"); // All of the above passert!( expr!( (4) Mul( (3) Add( (2) Lsh( (1) And([Neg] 1) ) Rsh([Deref] 2) ) Add([Not] 3) ) Mul(4) ), "4 * 3 + 2 << 1 & -1 >> *2 + !3 * 4" ); }