use crate::asm::{*, expr::ValueType};
use crate::asm::expr::*;
#[test]
fn test_empty_source() {
if let Err(e) = assemble("test.asm", &mut "".as_bytes(), Default::default()) {
panic!("{:?}", e);
}
}
#[test]
fn test_shebang() {
if let Err(e) = assemble("test.asm", &mut "#!csx -s".as_bytes(), Default::default()) {
panic!("{:?}", e);
}
if let Ok(_) = assemble("test.asm", &mut "\n#!csx -s".as_bytes(), Default::default()) {
panic!();
}
}
#[test]
fn test_segment() {
if let Err(e) = assemble("test.asm", &mut "segMent tExT".as_bytes(), Default::default()) {
panic!("{:?}", e);
}
match assemble("test.asm", &mut "segment eegrf".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
assert!(matches!(e.kind, AsmErrorKind::ArgumentInvalidType { index: None, got: ArgumentType::Imm, expected: &[ArgumentType::Segment] }));
assert_eq!(e.line_num, 1);
assert_eq!(e.pos, None);
assert!(e.inner_err.is_none());
}
}
match assemble("test.asm", &mut "segment text dsdef".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
assert!(matches!(e.kind, AsmErrorKind::ExtraContentAfterArgs));
assert_eq!(e.line_num, 1);
assert_eq!(e.pos, Some(13));
assert!(e.inner_err.is_none());
}
}
match assemble("test.asm", &mut "segment text, dsdef".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
assert!(matches!(e.kind, AsmErrorKind::ArgsExpectedCount(&[1])));
assert_eq!(e.line_num, 1);
assert_eq!(e.pos, Some(0));
assert!(e.inner_err.is_none());
}
}
match assemble("test.asm", &mut "segment tExT\nsegment text".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
assert!(matches!(e.kind, AsmErrorKind::SegmentAlreadyCompleted));
assert_eq!(e.line_num, 2);
assert_eq!(e.pos, None);
assert!(e.inner_err.is_none());
}
}
if let Err(e) = assemble("test.asm", &mut "segment tExT\nsegment data\nsegment rodata\nsegment bss".as_bytes(), Default::default()) {
panic!("{:?}", e);
}
match assemble("test.asm", &mut "segment tExT\nlabel: segment data".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
assert!(matches!(e.kind, AsmErrorKind::LabelOnSegmentLine));
assert_eq!(e.line_num, 2);
assert_eq!(e.pos, None);
assert!(e.inner_err.is_none());
}
}
}
#[test]
fn test_ins_outside_text_seg() {
match assemble("test.asm", &mut "mov eax, 0".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
assert!(matches!(e.kind, AsmErrorKind::InstructionOutsideOfTextSegment));
assert_eq!(e.line_num, 1);
assert_eq!(e.pos, None);
assert!(e.inner_err.is_none());
}
}
match assemble("test.asm", &mut "segment data\nmov eax, 0".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
assert!(matches!(e.kind, AsmErrorKind::InstructionOutsideOfTextSegment));
assert_eq!(e.line_num, 2);
assert_eq!(e.pos, None);
assert!(e.inner_err.is_none());
}
}
match assemble("test.asm", &mut "segment rodata\nmov eax, 0".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
assert!(matches!(e.kind, AsmErrorKind::InstructionOutsideOfTextSegment));
assert_eq!(e.line_num, 2);
assert_eq!(e.pos, None);
assert!(e.inner_err.is_none());
}
}
match assemble("test.asm", &mut "segment bss\nmov eax, 0".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
assert!(matches!(e.kind, AsmErrorKind::InstructionOutsideOfTextSegment));
assert_eq!(e.line_num, 2);
assert_eq!(e.pos, None);
assert!(e.inner_err.is_none());
}
}
}
#[test]
fn test_no_arg_ops() {
if let Err(e) = assemble("test.asm", &mut "segment text\nnop".as_bytes(), Default::default()) {
panic!("{:?}", e);
}
match assemble("test.asm", &mut "segment text\nnop 0".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
assert!(matches!(e.kind, AsmErrorKind::ArgsExpectedCount(&[0])));
assert_eq!(e.line_num, 2);
assert_eq!(e.pos, None);
assert!(e.inner_err.is_none());
}
}
}
#[test]
fn test_binary_ops() {
if let Err(e) = assemble("test.asm", &mut "segment text\nmov rax, 0".as_bytes(), Default::default()) {
panic!("{:?}", e);
}
match assemble("test.asm", &mut "segment text\n mov rax, 0 0".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
assert!(matches!(e.kind, AsmErrorKind::ExtraContentAfterArgs));
assert_eq!(e.line_num, 2);
assert_eq!(e.pos, Some(14));
assert!(e.inner_err.is_none());
}
}
if let Err(e) = assemble("test.asm", &mut "segment text\n mov rax,0;0".as_bytes(), Default::default()) {
panic!("{:?}", e);
}
}
#[test]
fn test_assert() {
if let Err(e) = assemble("test.asm", &mut "static_assert true".as_bytes(), Default::default()) {
panic!("{:?}", e);
}
match assemble("test.asm", &mut "static_assert qword true".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
assert!(matches!(e.kind, AsmErrorKind::SizeSpecNotAllowed { index: None }));
assert_eq!(e.line_num, 1);
assert_eq!(e.pos, None);
assert!(e.inner_err.is_none());
}
}
match assemble("test.asm", &mut " static_assert false".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
assert!(matches!(e.kind, AsmErrorKind::AssertFailure));
assert_eq!(e.line_num, 1);
assert_eq!(e.pos, Some(2));
assert!(e.inner_err.is_none());
}
}
match assemble("test.asm", &mut " static_assert abc".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
assert!(matches!(e.kind, AsmErrorKind::FailedCriticalExpression(_)));
assert_eq!(e.line_num, 1);
assert_eq!(e.pos, None);
assert!(e.inner_err.is_none());
}
}
match assemble("test.asm", &mut " static_assert 1; hello".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
assert!(matches!(e.kind, AsmErrorKind::ValueInvalidType { index: None, got: ValueType::Integer, expected: &[ValueType::Logical] }));
assert_eq!(e.line_num, 1);
assert_eq!(e.pos, None);
assert!(e.inner_err.is_none());
}
}
match assemble("test.asm", &mut " static_assert eax; hello".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
assert!(matches!(e.kind, AsmErrorKind::ArgumentInvalidType { index: None, got: ArgumentType::CPURegister, expected: &[ArgumentType::Imm] }));
assert_eq!(e.line_num, 1);
assert_eq!(e.pos, None);
assert!(e.inner_err.is_none());
}
}
match assemble("test.asm", &mut " static_assert;".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
assert!(matches!(e.kind, AsmErrorKind::ArgsExpectedCount(&[1])));
assert_eq!(e.line_num, 1);
assert_eq!(e.pos, Some(2));
assert!(e.inner_err.is_none());
}
}
}
#[test]
fn test_double_global_extern() {
match assemble("test.asm", &mut "global abc\nglobal abc".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
match e.kind {
AsmErrorKind::RedundantGlobalOrExternDecl { prev_line_num } => assert_eq!(prev_line_num, 1),
_ => panic!("{:?}", e),
}
assert_eq!(e.line_num, 2);
assert_eq!(e.pos, None);
assert!(e.inner_err.is_none());
}
}
match assemble("test.asm", &mut "extern abc\nextern abc".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
match e.kind {
AsmErrorKind::RedundantGlobalOrExternDecl { prev_line_num } => assert_eq!(prev_line_num, 1),
_ => panic!("{:?}", e),
}
assert_eq!(e.line_num, 2);
assert_eq!(e.pos, None);
assert!(e.inner_err.is_none());
}
}
match assemble("test.asm", &mut "global abc, abc".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
match e.kind {
AsmErrorKind::RedundantGlobalOrExternDecl { prev_line_num } => assert_eq!(prev_line_num, 1),
_ => panic!("{:?}", e),
}
assert_eq!(e.line_num, 1);
assert_eq!(e.pos, None);
assert!(e.inner_err.is_none());
}
}
match assemble("test.asm", &mut "extern abc, abc".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
match e.kind {
AsmErrorKind::RedundantGlobalOrExternDecl { prev_line_num } => assert_eq!(prev_line_num, 1),
_ => panic!("{:?}", e),
}
assert_eq!(e.line_num, 1);
assert_eq!(e.pos, None);
assert!(e.inner_err.is_none());
}
}
}
#[test]
fn test_addr_8bit() {
match assemble("test.asm", &mut "segment text\nmov eax, [ah + al]".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
match e.kind {
AsmErrorKind::BadAddress(BadAddress::SizeUnsupported) => (),
_ => panic!("{:?}", e),
}
assert_eq!(e.line_num, 2);
assert_eq!(e.pos, Some(9));
assert!(e.inner_err.is_none());
}
}
match assemble("test.asm", &mut "segment text\nmov eax, [al]".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
match e.kind {
AsmErrorKind::BadAddress(BadAddress::SizeUnsupported) => (),
_ => panic!("{:?}", e),
}
assert_eq!(e.line_num, 2);
assert_eq!(e.pos, Some(9));
assert!(e.inner_err.is_none());
}
}
match assemble("test.asm", &mut "segment text\nmov eax, [byte 0]".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
match e.kind {
AsmErrorKind::BadAddress(BadAddress::SizeUnsupported) => (),
_ => panic!("{:?}", e),
}
assert_eq!(e.line_num, 2);
assert_eq!(e.pos, Some(9));
assert!(e.inner_err.is_none());
}
}
}
#[test]
fn test_bad_addr() {
match assemble("test.asm", &mut "segment text\nlea rax, [rax + rbx + rcx]".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
match e.kind {
AsmErrorKind::BadAddress(BadAddress::InvalidRegMults) => (),
_ => panic!("{:?}", e),
}
assert_eq!(e.line_num, 2);
assert_eq!(e.pos, Some(9));
assert!(e.inner_err.is_none());
}
}
match assemble("test.asm", &mut "segment text\nlea rax, [dword bx]".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
match e.kind {
AsmErrorKind::BadAddress(BadAddress::ConflictingSizes) => (),
_ => panic!("{:?}", e),
}
assert_eq!(e.line_num, 2);
assert_eq!(e.pos, Some(9));
assert!(e.inner_err.is_none());
}
}
match assemble("test.asm", &mut "segment text\nlea rax, [bx + rcx]".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
match e.kind {
AsmErrorKind::BadAddress(BadAddress::ConflictingSizes) => (),
_ => panic!("{:?}", e),
}
assert_eq!(e.line_num, 2);
assert_eq!(e.pos, Some(9));
assert!(e.inner_err.is_none());
}
}
match assemble("test.asm", &mut "segment text\nlea rax, [2*rax + 2*rbx]".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
match e.kind {
AsmErrorKind::BadAddress(BadAddress::InvalidRegMults) => (),
_ => panic!("{:?}", e),
}
assert_eq!(e.line_num, 2);
assert_eq!(e.pos, Some(9));
assert!(e.inner_err.is_none());
}
}
match assemble("test.asm", &mut "segment text\nlea rax, [6*rax]".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
match e.kind {
AsmErrorKind::BadAddress(BadAddress::InvalidRegMults) => (),
_ => panic!("{:?}", e),
}
assert_eq!(e.line_num, 2);
assert_eq!(e.pos, Some(9));
assert!(e.inner_err.is_none());
}
}
match assemble("test.asm", &mut "segment text\nlea rax, [0*eax + 1*rax]".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
match e.kind {
AsmErrorKind::BadAddress(BadAddress::ConflictingSizes) => (),
_ => panic!("{:?}", e),
}
assert_eq!(e.line_num, 2);
assert_eq!(e.pos, Some(9));
assert!(e.inner_err.is_none());
}
}
match assemble("test.asm", &mut "segment text\nlea rax, [1 / eax]".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
match e.kind {
AsmErrorKind::BadAddress(BadAddress::RegIllegalOp) => (),
_ => panic!("{:?}", e),
}
assert_eq!(e.line_num, 2);
assert_eq!(e.pos, Some(9));
assert!(e.inner_err.is_none());
}
}
match assemble("test.asm", &mut "segment text\nlea rax, [eax >> 2]".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
match e.kind {
AsmErrorKind::BadAddress(BadAddress::RegIllegalOp) => (),
_ => panic!("{:?}", e),
}
assert_eq!(e.line_num, 2);
assert_eq!(e.pos, Some(9));
assert!(e.inner_err.is_none());
}
}
match assemble("test.asm", &mut "segment text\nlea rax, [eax * eax]".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => { assert_eq!(e.line_num, 2);
assert_eq!(e.pos, Some(9));
assert!(e.inner_err.is_none());
}
}
match assemble("test.asm", &mut "segment text\nlea rax, [eax * ebx]".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => { assert_eq!(e.line_num, 2);
assert_eq!(e.pos, Some(9));
assert!(e.inner_err.is_none());
}
}
match assemble("test.asm", &mut "segment text\nlea rax, [ebx * eax]".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => { assert_eq!(e.line_num, 2);
assert_eq!(e.pos, Some(9));
assert!(e.inner_err.is_none());
}
}
match assemble("test.asm", &mut "segment text\nlea rax, [2.0 * rax]".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
match e.kind {
AsmErrorKind::BadAddress(BadAddress::RegMultNotCriticalExpr(reason)) => match reason {
EvalError::Illegal(IllegalReason::IncompatibleTypes(OP::Mul, ValueType::Float, ValueType::Integer)) => (),
_ => panic!("{:?}", reason),
}
_ => panic!("{:?}", e.kind),
}
assert_eq!(e.line_num, 2);
assert_eq!(e.pos, Some(9));
assert!(e.inner_err.is_none());
}
}
match assemble("test.asm", &mut "segment text\nlea rax, []".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
match e.kind {
AsmErrorKind::BadAddress(BadAddress::BadBase) => (),
_ => panic!("{:?}", e.kind),
}
assert_eq!(e.line_num, 2);
assert_eq!(e.pos, Some(9));
let inner = *e.inner_err.unwrap();
match inner.kind {
AsmErrorKind::ExpectedExpr => (),
_ => panic!("{:?}", inner.kind),
}
assert_eq!(inner.line_num, 2);
assert_eq!(inner.pos, Some(10));
assert!(inner.inner_err.is_none());
}
}
match assemble("test.asm", &mut "segment text\nlea rax, [dword]".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
match e.kind {
AsmErrorKind::BadAddress(BadAddress::BadBase) => (),
_ => panic!("{:?}", e.kind),
}
assert_eq!(e.line_num, 2);
assert_eq!(e.pos, Some(9));
let inner = *e.inner_err.unwrap();
match inner.kind {
AsmErrorKind::ExpectedExpr => (),
_ => panic!("{:?}", inner.kind),
}
assert_eq!(inner.line_num, 2);
assert_eq!(inner.pos, Some(15));
assert!(inner.inner_err.is_none());
}
}
}
#[test]
fn test_late_expr_errors() {
match assemble("test.asm", &mut "val: equ 1.0 + foo\nfoo: equ 2\n\n\n\n\n\n\n\n\n\n".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
match e.kind {
AsmErrorKind::ExprIllegalError(IllegalReason::IncompatibleTypes(OP::Add, ValueType::Float, ValueType::Integer)) => (),
k => panic!("{:?}", k),
}
assert_eq!(e.line_num, 1);
assert_eq!(e.pos, None);
assert!(e.inner_err.is_none());
}
}
}
#[test]
fn test_times_errors() {
match assemble("test.asm", &mut "segment text\n times dword 5 nop".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
match e.kind {
AsmErrorKind::SizeSpecNotAllowed { index: None } => (),
r => panic!("{:?}", r),
}
assert_eq!(e.line_num, 2);
assert_eq!(e.pos, Some(1));
assert!(e.inner_err.is_none());
}
}
match assemble("test.asm", &mut "segment text\n times eax nop".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
match e.kind {
AsmErrorKind::ArgumentInvalidType { index: None, got: ArgumentType::CPURegister, expected: &[ArgumentType::Imm] } => (),
k => panic!("{:?}", k),
}
assert_eq!(e.line_num, 2);
assert_eq!(e.pos, Some(3));
assert!(e.inner_err.is_none());
}
}
match assemble("test.asm", &mut "segment text\n times 5.6 nop".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
match e.kind {
AsmErrorKind::ValueInvalidType { index: None, got: ValueType::Float, expected: &[ValueType::Integer] } => (),
k => panic!("{:?}", k),
}
assert_eq!(e.line_num, 2);
assert_eq!(e.pos, Some(3));
assert!(e.inner_err.is_none());
}
}
}
#[test]
fn test_if_errors() {
match assemble("test.asm", &mut "segment text\n if dword true nop".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
match e.kind {
AsmErrorKind::SizeSpecNotAllowed { index: None } => (),
r => panic!("{:?}", r),
}
assert_eq!(e.line_num, 2);
assert_eq!(e.pos, Some(1));
assert!(e.inner_err.is_none());
}
}
match assemble("test.asm", &mut "segment text\n if eax nop".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
match e.kind {
AsmErrorKind::ArgumentInvalidType { index: None, got: ArgumentType::CPURegister, expected: &[ArgumentType::Imm] } => (),
k => panic!("{:?}", k),
}
assert_eq!(e.line_num, 2);
assert_eq!(e.pos, Some(3));
assert!(e.inner_err.is_none());
}
}
match assemble("test.asm", &mut "segment text\n if 5 nop".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
match e.kind {
AsmErrorKind::ValueInvalidType { index: None, got: ValueType::Integer, expected: &[ValueType::Logical] } => (),
k => panic!("{:?}", k),
}
assert_eq!(e.line_num, 2);
assert_eq!(e.pos, Some(3));
assert!(e.inner_err.is_none());
}
}
}
#[test]
fn test_value_unknown_size() {
match assemble("test.asm", &mut "segment text\npush 5".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
match e.kind {
AsmErrorKind::CouldNotDeduceOperandSize => (),
k => panic!("{:?}", k),
}
assert_eq!(e.line_num, 2);
assert_eq!(e.pos, None);
assert!(e.inner_err.is_none());
}
}
match assemble("test.asm", &mut "segment text\nmul 5".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
match e.kind {
AsmErrorKind::CouldNotDeduceOperandSize => (),
k => panic!("{:?}", k),
}
assert_eq!(e.line_num, 2);
assert_eq!(e.pos, None);
assert!(e.inner_err.is_none());
}
}
match assemble("test.asm", &mut "segment text\nimul 5".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
match e.kind {
AsmErrorKind::CouldNotDeduceOperandSize => (),
k => panic!("{:?}", k),
}
assert_eq!(e.line_num, 2);
assert_eq!(e.pos, None);
assert!(e.inner_err.is_none());
}
}
}
#[test]
fn test_kmov_wrong_size() {
match assemble("test.asm", &mut "segment text\nkmovb k1, al".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
match e.kind {
AsmErrorKind::ArgumentInvalidSize { index: Some(1), got: Size::Byte, expected } if expected == &[Size::Dword] => (),
k => panic!("{:?}", k),
}
assert_eq!(e.line_num, 2);
assert_eq!(e.pos, None);
assert!(e.inner_err.is_none());
}
}
match assemble("test.asm", &mut "segment text\nkmovb k1, bh".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
match e.kind {
AsmErrorKind::ArgumentInvalidSize { index: Some(1), got: Size::Byte, expected } if expected == &[Size::Dword] => (),
k => panic!("{:?}", k),
}
assert_eq!(e.line_num, 2);
assert_eq!(e.pos, None);
assert!(e.inner_err.is_none());
}
}
match assemble("test.asm", &mut "segment text\nkmovb k1, dword ptr [0]".as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
match e.kind {
AsmErrorKind::ArgumentInvalidSize { index: Some(1), got: Size::Dword, expected } if expected == &[Size::Byte] => (),
k => panic!("{:?}", k),
}
assert_eq!(e.line_num, 2);
assert_eq!(e.pos, None);
assert!(e.inner_err.is_none());
}
}
}
#[test]
fn test_prefixed_suggestion() {
for instruction in &["stos", "cmps", "movs"] {
for prefix in &["", "rep", "repe", "repne", "repz", "repnz", "lock"] {
println!("prefix: {}", prefix);
match assemble("test.asm", &mut format!("segment text\n{} {}", prefix, instruction).as_bytes(), Default::default()) {
Ok(_) => panic!(),
Err(e) => {
match e.kind {
AsmErrorKind::SuggestInstruction { .. } => (),
k => panic!("{:?}", k),
}
assert_eq!(e.line_num, 2);
assert_eq!(e.pos, None);
assert!(e.inner_err.is_none());
}
}
}
}
}