Files
stationeers_lang/rust_compiler/libs/compiler/src/test/tuple_literals.rs

1355 lines
30 KiB
Rust

#[cfg(test)]
mod test {
use indoc::indoc;
use pretty_assertions::assert_eq;
#[test]
fn test_tuple_literal_declaration() -> anyhow::Result<()> {
let compiled = compile!(
check
r#"
let (x, y) = (1, 2);
"#
);
assert!(
compiled.errors.is_empty(),
"Expected no errors, got: {:?}",
compiled.errors
);
assert_eq!(
compiled.output,
indoc! {
"
j main
main:
move r8 1
move r9 2
"
}
);
Ok(())
}
#[test]
fn test_tuple_literal_declaration_with_underscore() -> anyhow::Result<()> {
let compiled = compile!(
check
r#"
let (x, _) = (1, 2);
"#
);
assert!(
compiled.errors.is_empty(),
"Expected no errors, got: {:?}",
compiled.errors
);
assert_eq!(
compiled.output,
indoc! {
"
j main
main:
move r8 1
"
}
);
Ok(())
}
#[test]
fn test_tuple_literal_assignment() -> anyhow::Result<()> {
let compiled = compile!(
check
r#"
let x = 0;
let y = 0;
(x, y) = (5, 10);
"#
);
assert!(
compiled.errors.is_empty(),
"Expected no errors, got: {:?}",
compiled.errors
);
assert_eq!(
compiled.output,
indoc! {
"
j main
main:
move r8 0
move r9 0
move r8 5
move r9 10
"
}
);
Ok(())
}
#[test]
fn test_tuple_literal_with_variables() -> anyhow::Result<()> {
let compiled = compile!(
check
r#"
let a = 42;
let b = 99;
let (x, y) = (a, b);
"#
);
assert!(
compiled.errors.is_empty(),
"Expected no errors, got: {:?}",
compiled.errors
);
assert_eq!(
compiled.output,
indoc! {
"
j main
main:
move r8 42
move r9 99
move r10 r8
move r11 r9
"
}
);
Ok(())
}
#[test]
fn test_tuple_literal_three_elements() -> anyhow::Result<()> {
let compiled = compile!(
check
r#"
let (x, y, z) = (1, 2, 3);
"#
);
assert!(
compiled.errors.is_empty(),
"Expected no errors, got: {:?}",
compiled.errors
);
assert_eq!(
compiled.output,
indoc! {
"
j main
main:
move r8 1
move r9 2
move r10 3
"
}
);
Ok(())
}
#[test]
fn test_tuple_literal_assignment_with_underscore() -> anyhow::Result<()> {
let compiled = compile!(
check
r#"
let i = 0;
let x = 123;
(i, _) = (456, 789);
"#
);
assert!(
compiled.errors.is_empty(),
"Expected no errors, got: {:?}",
compiled.errors
);
assert_eq!(
compiled.output,
indoc! {
"
j main
main:
move r8 0
move r9 123
move r8 456
"
}
);
Ok(())
}
#[test]
fn test_tuple_return_simple() -> anyhow::Result<()> {
let compiled = compile!(
check
r#"
fn getPair() {
return (10, 20);
};
let (x, y) = getPair();
"#
);
assert!(
compiled.errors.is_empty(),
"Expected no errors, got: {:?}",
compiled.errors
);
assert_eq!(
compiled.output,
indoc! {
"
j main
getPair:
push sp
push ra
push 10
push 20
sub r0 sp 4
get r0 db r0
move r15 r0
j __internal_L1
__internal_L1:
sub r0 sp 3
get ra db r0
j ra
main:
jal getPair
pop r9
pop r8
move sp r15
"
}
);
Ok(())
}
#[test]
fn test_tuple_return_with_underscore() -> anyhow::Result<()> {
let compiled = compile!(
check
r#"
fn getPair() {
return (5, 15);
};
let (x, _) = getPair();
"#
);
assert!(
compiled.errors.is_empty(),
"Expected no errors, got: {:?}",
compiled.errors
);
assert_eq!(
compiled.output,
indoc! {
"
j main
getPair:
push sp
push ra
push 5
push 15
sub r0 sp 4
get r0 db r0
move r15 r0
j __internal_L1
__internal_L1:
sub r0 sp 3
get ra db r0
j ra
main:
jal getPair
pop r0
pop r8
move sp r15
"
}
);
Ok(())
}
#[test]
fn test_tuple_return_three_elements() -> anyhow::Result<()> {
let compiled = compile!(
check
r#"
fn getTriple() {
return (1, 2, 3);
};
let (a, b, c) = getTriple();
"#
);
assert!(
compiled.errors.is_empty(),
"Expected no errors, got: {:?}",
compiled.errors
);
assert_eq!(
compiled.output,
indoc! {
"
j main
getTriple:
push sp
push ra
push 1
push 2
push 3
sub r0 sp 5
get r0 db r0
move r15 r0
j __internal_L1
__internal_L1:
sub r0 sp 4
get ra db r0
j ra
main:
jal getTriple
pop r10
pop r9
pop r8
move sp r15
"
}
);
Ok(())
}
#[test]
fn test_tuple_return_assignment() -> anyhow::Result<()> {
let compiled = compile!(
check
r#"
fn getPair() {
return (42, 84);
};
let i = 1;
let j = 2;
(i, j) = getPair();
"#
);
assert!(
compiled.errors.is_empty(),
"Expected no errors, got: {:?}",
compiled.errors
);
assert_eq!(
compiled.output,
indoc! {
"
j main
getPair:
push sp
push ra
push 42
push 84
sub r0 sp 4
get r0 db r0
move r15 r0
j __internal_L1
__internal_L1:
sub r0 sp 3
get ra db r0
j ra
main:
move r8 1
move r9 2
jal getPair
pop r9
pop r8
move sp r15
"
}
);
Ok(())
}
#[test]
fn test_tuple_return_mismatch() -> anyhow::Result<()> {
let errors = compile!(
result
r#"
fn doSomething() {
return (1, 2, 3);
};
let (x, y) = doSomething();
"#
);
// Should have exactly one error about tuple size mismatch
assert_eq!(errors.len(), 1);
// Check for the specific TupleSizeMismatch error
match &errors[0] {
crate::Error::TupleSizeMismatch(expected_size, actual_count, _) => {
assert_eq!(*expected_size, 3);
assert_eq!(*actual_count, 2);
}
e => panic!("Expected TupleSizeMismatch error, got: {:?}", e),
}
Ok(())
}
#[test]
fn test_tuple_return_called_by_non_tuple_return() -> anyhow::Result<()> {
let compiled = compile!(
check
r#"
fn doSomething() {
return (1, 2);
};
fn doSomethingElse() {
let (x, y) = doSomething();
return y;
};
let returnedValue = doSomethingElse();
"#
);
assert!(
compiled.errors.is_empty(),
"Expected no errors, got: {:?}",
compiled.errors
);
assert_eq!(
compiled.output,
indoc! {
"
j main
doSomething:
push sp
push ra
push 1
push 2
sub r0 sp 4
get r0 db r0
move r15 r0
j __internal_L1
__internal_L1:
sub r0 sp 3
get ra db r0
j ra
doSomethingElse:
push sp
push ra
jal doSomething
pop r9
pop r8
move sp r15
move r15 r9
j __internal_L2
__internal_L2:
pop ra
pop sp
j ra
main:
jal doSomethingElse
move r8 r15
"
}
);
Ok(())
}
#[test]
fn test_non_tuple_return_called_by_tuple_return() -> anyhow::Result<()> {
let compiled = compile!(
check
r#"
fn getValue() {
return 42;
};
fn getTuple() {
let x = getValue();
return (x, x);
};
let (a, b) = getTuple();
"#
);
assert!(
compiled.errors.is_empty(),
"Expected no errors, got: {:?}",
compiled.errors
);
assert_eq!(
compiled.output,
indoc! {
"
j main
getValue:
push sp
push ra
move r15 42
j __internal_L1
__internal_L1:
pop ra
pop sp
j ra
getTuple:
push sp
push ra
jal getValue
move r8 r15
push r8
push r8
sub r0 sp 4
get r0 db r0
move r15 r0
j __internal_L2
__internal_L2:
sub r0 sp 3
get ra db r0
j ra
main:
jal getTuple
pop r9
pop r8
move sp r15
"
}
);
Ok(())
}
#[test]
fn test_tuple_literal_size_mismatch() -> anyhow::Result<()> {
let errors = compile!(
result
r#"
let (x, y) = (1, 2, 3);
"#
);
// Should have exactly one error about tuple size mismatch
assert_eq!(errors.len(), 1);
assert!(matches!(
errors[0],
crate::Error::TupleSizeMismatch(_, _, _)
));
Ok(())
}
#[test]
fn test_multiple_tuple_returns_in_function() -> anyhow::Result<()> {
// Test multiple return paths in tuple-returning function
let compiled = compile!(
check
r#"
fn getValue(x) {
if (x) {
return (1, 2);
} else {
return (3, 4);
}
};
let (a, b) = getValue(1);
"#
);
assert!(
compiled.errors.is_empty(),
"Expected no errors, got: {:?}",
compiled.errors
);
assert_eq!(
compiled.output,
indoc! {
"
j main
getValue:
pop r8
push sp
push ra
beqz r8 __internal_L3
push 1
push 2
sub r0 sp 4
get r0 db r0
move r15 r0
j __internal_L1
j __internal_L2
__internal_L3:
push 3
push 4
sub r0 sp 4
get r0 db r0
move r15 r0
j __internal_L1
__internal_L2:
__internal_L1:
sub r0 sp 3
get ra db r0
j ra
main:
push 1
jal getValue
pop r9
pop r8
move sp r15
"
},
);
Ok(())
}
#[test]
fn test_tuple_return_with_expression() -> anyhow::Result<()> {
let compiled = compile!(
check
r#"
fn add(x, y) {
return (x, y);
};
let (a, b) = add(5, 10);
"#
);
assert!(
compiled.errors.is_empty(),
"Expected no errors, got: {:?}",
compiled.errors
);
assert_eq!(
compiled.output,
indoc! {
"
j main
add:
pop r8
pop r9
push sp
push ra
push r9
push r8
sub r0 sp 4
get r0 db r0
move r15 r0
j __internal_L1
__internal_L1:
sub r0 sp 3
get ra db r0
j ra
main:
push 5
push 10
jal add
pop r9
pop r8
move sp r15
"
}
);
Ok(())
}
#[test]
fn test_nested_function_tuple_calls() -> anyhow::Result<()> {
let compiled = compile!(
check
r#"
fn inner() {
return (1, 2);
};
fn outer() {
let (x, y) = inner();
return (y, x);
};
let (a, b) = outer();
"#
);
assert!(
compiled.errors.is_empty(),
"Expected no errors, got: {:?}",
compiled.errors
);
assert_eq!(
compiled.output,
indoc! {
"
j main
inner:
push sp
push ra
push 1
push 2
sub r0 sp 4
get r0 db r0
move r15 r0
j __internal_L1
__internal_L1:
sub r0 sp 3
get ra db r0
j ra
outer:
push sp
push ra
jal inner
pop r9
pop r8
move sp r15
push r9
push r8
sub r0 sp 4
get r0 db r0
move r15 r0
j __internal_L2
__internal_L2:
sub r0 sp 3
get ra db r0
j ra
main:
jal outer
pop r9
pop r8
move sp r15
"
}
);
Ok(())
}
#[test]
fn test_tuple_literal_with_constant_expressions() -> anyhow::Result<()> {
let compiled = compile!(
check
r#"
let (a, b) = (1 + 2, 3 * 4);
"#
);
assert!(
compiled.errors.is_empty(),
"Expected no errors, got: {:?}",
compiled.errors
);
assert_eq!(
compiled.output,
indoc! {
"
j main
main:
move r8 3
move r9 12
"
}
);
Ok(())
}
#[test]
fn test_tuple_literal_with_variable_expressions() -> anyhow::Result<()> {
let compiled = compile!(
check
r#"
let x = 5;
let y = 10;
let (a, b) = (x + 1, y * 2);
"#
);
assert!(
compiled.errors.is_empty(),
"Expected no errors, got: {:?}",
compiled.errors
);
assert_eq!(
compiled.output,
indoc! {
"
j main
main:
move r8 5
move r9 10
add r1 r8 1
move r10 r1
mul r2 r9 2
move r11 r2
"
}
);
Ok(())
}
#[test]
fn test_tuple_assignment_with_expressions() -> anyhow::Result<()> {
let compiled = compile!(
check
r#"
let a = 0;
let b = 0;
let x = 5;
(a, b) = (x + 1, x * 2);
"#
);
assert!(
compiled.errors.is_empty(),
"Expected no errors, got: {:?}",
compiled.errors
);
assert_eq!(
compiled.output,
indoc! {
"
j main
main:
move r8 0
move r9 0
move r10 5
add r1 r10 1
move r8 r1
mul r2 r10 2
move r9 r2
"
}
);
Ok(())
}
#[test]
fn test_tuple_literal_with_function_calls() -> anyhow::Result<()> {
let compiled = compile!(
check
r#"
fn getValue() { return 42; };
fn getOther() { return 99; };
let (a, b) = (getValue(), getOther());
"#
);
assert!(
compiled.errors.is_empty(),
"Expected no errors, got: {:?}",
compiled.errors
);
assert_eq!(
compiled.output,
indoc! {
"
j main
getValue:
push sp
push ra
move r15 42
j __internal_L1
__internal_L1:
pop ra
pop sp
j ra
getOther:
push sp
push ra
move r15 99
j __internal_L2
__internal_L2:
pop ra
pop sp
j ra
main:
push r8
jal getValue
pop r8
move r1 r15
move r8 r1
push r8
push r9
jal getOther
pop r9
pop r8
move r2 r15
move r9 r2
"
}
);
Ok(())
}
#[test]
fn test_tuple_with_logical_expressions() -> anyhow::Result<()> {
let compiled = compile!(
check
r#"
let x = 1;
let y = 0;
let (a, b) = (x && y, x || y);
"#
);
assert!(
compiled.errors.is_empty(),
"Expected no errors, got: {:?}",
compiled.errors
);
assert_eq!(
compiled.output,
indoc! {
"
j main
main:
move r8 1
move r9 0
and r1 r8 r9
move r10 r1
or r2 r8 r9
move r11 r2
"
}
);
Ok(())
}
#[test]
fn test_tuple_with_comparison_expressions() -> anyhow::Result<()> {
let compiled = compile!(
check
r#"
let x = 5;
let y = 10;
let (a, b) = (x > y, x < y);
"#
);
assert!(
compiled.errors.is_empty(),
"Expected no errors, got: {:?}",
compiled.errors
);
assert_eq!(
compiled.output,
indoc! {
"
j main
main:
move r8 5
move r9 10
sgt r1 r8 r9
move r10 r1
slt r2 r8 r9
move r11 r2
"
}
);
Ok(())
}
#[test]
fn test_tuple_with_device_property_access() -> anyhow::Result<()> {
let compiled = compile!(
check
r#"
device sensor = "d0";
device display = "d1";
let (temp, pressure) = (sensor.Temperature, sensor.Pressure);
"#
);
assert!(
compiled.errors.is_empty(),
"Expected no errors, got: {:?}",
compiled.errors
);
assert_eq!(
compiled.output,
indoc! {
"
j main
main:
l r1 d0 Temperature
move r8 r1
l r2 d0 Pressure
move r9 r2
"
}
);
Ok(())
}
#[test]
fn test_tuple_with_device_property_and_function_call() -> anyhow::Result<()> {
let compiled = compile!(
check
r#"
device self = "db";
fn getY() {
return 42;
}
let (x, y) = (self.Setting, getY());
"#
);
assert!(
compiled.errors.is_empty(),
"Expected no errors, got: {:?}",
compiled.errors
);
assert_eq!(
compiled.output,
indoc! {
"
j main
getY:
push sp
push ra
move r15 42
j __internal_L1
__internal_L1:
pop ra
pop sp
j ra
main:
l r1 db Setting
move r8 r1
push r8
push r9
jal getY
pop r9
pop r8
move r2 r15
move r9 r2
"
}
);
Ok(())
}
#[test]
fn test_tuple_with_function_call_expressions() -> anyhow::Result<()> {
let compiled = compile!(
check
r#"
fn getValue() { return 10; }
fn getOther() { return 20; }
let (a, b) = (getValue() + 5, getOther() * 2);
"#
);
assert!(
compiled.errors.is_empty(),
"Expected no errors, got: {:?}",
compiled.errors
);
assert_eq!(
compiled.output,
indoc! {
"
j main
getValue:
push sp
push ra
move r15 10
j __internal_L1
__internal_L1:
pop ra
pop sp
j ra
getOther:
push sp
push ra
move r15 20
j __internal_L2
__internal_L2:
pop ra
pop sp
j ra
main:
push r8
jal getValue
pop r8
move r1 r15
add r2 r1 5
move r8 r2
push r8
push r9
jal getOther
pop r9
pop r8
move r3 r15
mul r4 r3 2
move r9 r4
"
}
);
Ok(())
}
#[test]
fn test_tuple_with_stack_spillover() -> anyhow::Result<()> {
let compiled = compile!(
check
r#"
fn get8() {
return (1, 2, 3, 4, 5, 6, 7, 8);
}
let (a, b, c, d, e, f, g, h) = get8();
let sum = a + h;
"#
);
assert!(
compiled.errors.is_empty(),
"Expected no errors, got: {:?}",
compiled.errors
);
assert_eq!(
compiled.output,
indoc! {
"
j main
get8:
push sp
push ra
push 1
push 2
push 3
push 4
push 5
push 6
push 7
push 8
sub r0 sp 10
get r0 db r0
move r15 r0
j __internal_L1
__internal_L1:
sub r0 sp 9
get ra db r0
j ra
main:
jal get8
pop r0
sub r0 sp 0
put db r0 r0
pop r14
pop r13
pop r12
pop r11
pop r10
pop r9
pop r8
move sp r15
sub r0 sp 1
get r1 db r0
add r2 r8 r1
push r2
sub sp sp 2
"
}
);
Ok(())
}
#[test]
fn test_tuple_return_in_loop() -> anyhow::Result<()> {
let compiled = compile!(
check
r#"
fn getValues(i) {
return (i, i * 2);
};
let sum = 0;
let i = 0;
loop {
let (a, b) = getValues(i);
sum = sum + a + b;
i = i + 1;
if (i > 3) {
break;
}
}
"#
);
assert!(
compiled.errors.is_empty(),
"Expected no errors, got: {:?}",
compiled.errors
);
assert_eq!(
compiled.output,
indoc! {
"
j main
getValues:
pop r8
push sp
push ra
push r8
mul r1 r8 2
push r1
sub r0 sp 4
get r0 db r0
move r15 r0
j __internal_L1
__internal_L1:
sub r0 sp 3
get ra db r0
j ra
main:
move r8 0
move r9 0
__internal_L2:
push r9
jal getValues
pop r11
pop r10
move sp r15
add r1 r8 r10
add r2 r1 r11
move r8 r2
add r3 r9 1
move r9 r3
sgt r4 r9 3
beqz r4 __internal_L4
j __internal_L3
__internal_L4:
j __internal_L2
__internal_L3:
"
}
);
Ok(())
}
#[test]
fn test_tuple_all_forms_combined() -> anyhow::Result<()> {
// Test all three tuple forms in one test:
// 1. Tuple literal declaration: let (x, y) = (1, 2);
// 2. Tuple literal assignment: (x, y) = (3, 4);
// 3. Function return tuple: let (a, b) = func();
let compiled = compile!(
check
r#"
fn swap(x, y) {
return (y, x);
};
let (a, b) = (10, 20); // Literal declaration
(a, b) = (30, 40); // Literal assignment
let (c, d) = swap(a, b); // Function return
"#
);
assert!(
compiled.errors.is_empty(),
"Expected no errors, got: {:?}",
compiled.errors
);
assert_eq!(
compiled.output,
indoc! {
"
j main
swap:
pop r8
pop r9
push sp
push ra
push r8
push r9
sub r0 sp 4
get r0 db r0
move r15 r0
j __internal_L1
__internal_L1:
sub r0 sp 3
get ra db r0
j ra
main:
move r8 10
move r9 20
move r8 30
move r9 40
push r8
push r9
jal swap
pop r11
pop r10
move sp r15
"
}
);
Ok(())
}
}