#![allow(clippy::unwrap_used)]
#![allow(clippy::expect_used)]
#[cfg(test)]
#[test]
fn test_convert_match_arm_for_let_multi_stmt_if_last() {
let arms = vec![
MatchArm {
pattern: Pattern::Literal(Literal::I32(1)),
guard: None,
body: vec![
Stmt::Let {
name: "tmp".to_string(),
value: Expr::Literal(Literal::Str("x".to_string())),
declaration: true,
},
Stmt::If {
condition: Expr::Literal(Literal::Bool(true)),
then_block: vec![Stmt::Expr(Expr::Literal(Literal::Str(
"t".to_string(),
)))],
else_block: Some(vec![Stmt::Expr(Expr::Literal(Literal::Str(
"f".to_string(),
)))]),
},
],
},
wildcard_arm(vec![Stmt::Expr(Expr::Literal(Literal::Str("other".to_string())))]),
];
let ast = make_main(vec![
Stmt::Let {
name: "n".to_string(),
value: Expr::Literal(Literal::I32(1)),
declaration: true,
},
let_match("r", Expr::Variable("n".to_string()), arms),
]);
let ir = from_ast(&ast).expect("Multi-stmt ending with If should work");
assert_ir_is_sequence(&ir);
}
#[test]
fn test_convert_match_arm_for_let_multi_stmt_other_last() {
let arms = vec![
MatchArm {
pattern: Pattern::Literal(Literal::I32(1)),
guard: None,
body: vec![
Stmt::Expr(Expr::Literal(Literal::Str("first".to_string()))),
Stmt::Expr(Expr::FunctionCall {
name: "echo".to_string(),
args: vec![Expr::Literal(Literal::Str("done".to_string()))],
}),
],
},
wildcard_arm(vec![Stmt::Expr(Expr::Literal(Literal::Str("other".to_string())))]),
];
let ast = make_main(vec![
Stmt::Let {
name: "n".to_string(),
value: Expr::Literal(Literal::I32(1)),
declaration: true,
},
let_match("r", Expr::Variable("n".to_string()), arms),
]);
let ir = from_ast(&ast).expect("Multi-stmt other-last should work");
assert_ir_is_sequence(&ir);
}
#[test]
fn test_lower_let_if_with_else() {
let ast = make_main(vec![
Stmt::Let {
name: "x".to_string(),
value: Expr::Block(vec![Stmt::If {
condition: Expr::Literal(Literal::Bool(true)),
then_block: vec![Stmt::Expr(Expr::Literal(Literal::Str("yes".to_string())))],
else_block: Some(vec![Stmt::Expr(Expr::Literal(Literal::Str(
"no".to_string(),
)))]),
}]),
declaration: true,
},
]);
let ir = from_ast(&ast).expect("let-if with else should work");
assert_ir_is_sequence(&ir);
}
#[test]
fn test_lower_let_if_without_else() {
let ast = make_main(vec![
Stmt::Let {
name: "x".to_string(),
value: Expr::Block(vec![Stmt::If {
condition: Expr::Literal(Literal::Bool(false)),
then_block: vec![Stmt::Expr(Expr::Literal(Literal::Str("yes".to_string())))],
else_block: None,
}]),
declaration: true,
},
]);
let ir = from_ast(&ast).expect("let-if without else should work");
assert_ir_is_sequence(&ir);
}
#[test]
fn test_range_match_single_range_with_wildcard() {
let ast = make_main(vec![Stmt::Match {
scrutinee: Expr::Variable("n".to_string()),
arms: vec![
range_arm(
Literal::I32(0),
Literal::I32(10),
false,
vec![Stmt::Expr(Expr::FunctionCall {
name: "echo".to_string(),
args: vec![Expr::Literal(Literal::Str("low".to_string()))],
})],
),
wildcard_arm(vec![Stmt::Expr(Expr::FunctionCall {
name: "echo".to_string(),
args: vec![Expr::Literal(Literal::Str("high".to_string()))],
})]),
],
}]);
let ir = from_ast(&ast).expect("Range match should convert to if-chain");
assert_ir_is_sequence(&ir);
}
#[test]
fn test_range_match_multiple_ranges() {
let ast = make_main(vec![Stmt::Match {
scrutinee: Expr::Variable("n".to_string()),
arms: vec![
range_arm(
Literal::I32(0),
Literal::I32(5),
false,
vec![Stmt::Expr(Expr::FunctionCall {
name: "echo".to_string(),
args: vec![Expr::Literal(Literal::Str("low".to_string()))],
})],
),
range_arm(
Literal::I32(5),
Literal::I32(10),
true,
vec![Stmt::Expr(Expr::FunctionCall {
name: "echo".to_string(),
args: vec![Expr::Literal(Literal::Str("mid".to_string()))],
})],
),
wildcard_arm(vec![Stmt::Expr(Expr::FunctionCall {
name: "echo".to_string(),
args: vec![Expr::Literal(Literal::Str("high".to_string()))],
})]),
],
}]);
let ir = from_ast(&ast).expect("Multi-range match should convert");
assert_ir_is_sequence(&ir);
}
#[test]
fn test_range_match_only_wildcard_produces_else_body() {
let ast = make_main(vec![Stmt::Match {
scrutinee: Expr::Variable("n".to_string()),
arms: vec![wildcard_arm(vec![Stmt::Expr(Expr::FunctionCall {
name: "echo".to_string(),
args: vec![Expr::Literal(Literal::Str("any".to_string()))],
})])],
}]);
let ir = from_ast(&ast).expect("Wildcard-only match should work");
assert_ir_is_sequence(&ir);
}
#[test]
fn test_range_match_in_function_context() {
let ast = RestrictedAst {
functions: vec![Function {
name: "classify".to_string(),
params: vec![Parameter {
name: "n".to_string(),
param_type: Type::U32,
}],
return_type: Type::Str,
body: vec![Stmt::Match {
scrutinee: Expr::Variable("n".to_string()),
arms: vec![
range_arm(
Literal::I32(0),
Literal::I32(10),
false,
vec![Stmt::Return(Some(Expr::Literal(Literal::Str(
"low".to_string(),
))))],
),
wildcard_arm(vec![Stmt::Return(Some(Expr::Literal(Literal::Str(
"high".to_string(),
))))]),
],
}],
},
Function {
name: "main".to_string(),
params: vec![],
return_type: Type::Void,
body: vec![Stmt::Expr(Expr::FunctionCall {
name: "classify".to_string(),
args: vec![Expr::Literal(Literal::I32(5))],
})],
}],
entry_point: "main".to_string(),
};
let ir = from_ast(&ast).expect("Range match in function should convert");
assert_ir_is_sequence(&ir);
}
#[test]
fn test_lower_let_if_expr_simple() {
let ast = make_main(vec![Stmt::Let {
name: "x".to_string(),
value: Expr::FunctionCall {
name: "__if_expr".to_string(),
args: vec![
Expr::Literal(Literal::Bool(true)),
Expr::Literal(Literal::Str("yes".to_string())),
Expr::Literal(Literal::Str("no".to_string())),
],
},
declaration: true,
}]);
let ir = from_ast(&ast).expect("__if_expr should lower to If IR");
assert_ir_is_sequence(&ir);
}
#[test]
fn test_lower_let_if_expr_nested_then() {
let ast = make_main(vec![Stmt::Let {
name: "x".to_string(),
value: Expr::FunctionCall {
name: "__if_expr".to_string(),
args: vec![
Expr::Literal(Literal::Bool(true)),
Expr::FunctionCall {
name: "__if_expr".to_string(),
args: vec![
Expr::Literal(Literal::Bool(false)),
Expr::Literal(Literal::Str("a".to_string())),
Expr::Literal(Literal::Str("b".to_string())),
],
},
Expr::Literal(Literal::Str("c".to_string())),
],
},
declaration: true,
}]);
let ir = from_ast(&ast).expect("Nested __if_expr in then should work");
assert_ir_is_sequence(&ir);
}
#[test]
fn test_lower_let_if_expr_nested_else() {
let ast = make_main(vec![Stmt::Let {
name: "x".to_string(),
value: Expr::FunctionCall {
name: "__if_expr".to_string(),
args: vec![
Expr::Literal(Literal::Bool(true)),
Expr::Literal(Literal::Str("a".to_string())),
Expr::FunctionCall {
name: "__if_expr".to_string(),
args: vec![
Expr::Literal(Literal::Bool(false)),
Expr::Literal(Literal::Str("b".to_string())),
Expr::Literal(Literal::Str("c".to_string())),
],
},
],
},
declaration: true,
}]);
let ir = from_ast(&ast).expect("Nested __if_expr in else (elif chain) should work");
assert_ir_is_sequence(&ir);
}
#[test]
fn test_lower_let_if_expr_non_if_fn_in_then() {
let ast = make_main(vec![Stmt::Let {
name: "x".to_string(),
value: Expr::FunctionCall {
name: "__if_expr".to_string(),
args: vec![
Expr::Literal(Literal::Bool(true)),
Expr::FunctionCall {
name: "other_fn".to_string(),
args: vec![Expr::Literal(Literal::Str("arg".to_string()))],
},
Expr::Literal(Literal::Str("fallback".to_string())),
],
},
declaration: true,
}]);
let ir = from_ast(&ast).expect("Non-__if_expr fn in then branch should work");
assert_ir_is_sequence(&ir);
}
#[test]
fn test_lower_let_if_expr_non_if_fn_in_else() {
let ast = make_main(vec![Stmt::Let {
name: "x".to_string(),
value: Expr::FunctionCall {
name: "__if_expr".to_string(),
args: vec![
Expr::Literal(Literal::Bool(true)),
Expr::Literal(Literal::Str("a".to_string())),
Expr::FunctionCall {
name: "other_fn".to_string(),
args: vec![Expr::Literal(Literal::Str("arg".to_string()))],
},
],
},
declaration: true,
}]);
let ir = from_ast(&ast).expect("Non-__if_expr fn in else branch should work");
assert_ir_is_sequence(&ir);
}
#[test]
fn test_lower_return_if_expr_simple() {
let ast = RestrictedAst {
functions: vec![Function {
name: "get_val".to_string(),
params: vec![],
return_type: Type::U32,
body: vec![Stmt::Return(Some(Expr::FunctionCall {
name: "__if_expr".to_string(),
args: vec![
Expr::Literal(Literal::Bool(true)),
Expr::Literal(Literal::U32(1)),
Expr::Literal(Literal::U32(0)),
],
}))],
},
Function {
name: "main".to_string(),
params: vec![],
return_type: Type::Void,
body: vec![Stmt::Expr(Expr::FunctionCall {
name: "get_val".to_string(),
args: vec![],
})],
}],
entry_point: "main".to_string(),
};
let ir = from_ast(&ast).expect("return __if_expr should lower to If IR");
assert_ir_is_sequence(&ir);
}
#[test]
fn test_lower_return_if_expr_nested_then() {
let ast = RestrictedAst {
functions: vec![Function {
name: "get_val".to_string(),
params: vec![],
return_type: Type::U32,
body: vec![Stmt::Return(Some(Expr::FunctionCall {
name: "__if_expr".to_string(),
args: vec![
Expr::Literal(Literal::Bool(true)),
Expr::FunctionCall {
name: "__if_expr".to_string(),
args: vec![
Expr::Literal(Literal::Bool(false)),
Expr::Literal(Literal::U32(1)),
Expr::Literal(Literal::U32(2)),
],
},
Expr::Literal(Literal::U32(3)),
],
}))],
},
Function {
name: "main".to_string(),
params: vec![],
return_type: Type::Void,
body: vec![Stmt::Expr(Expr::FunctionCall {
name: "get_val".to_string(),
args: vec![],
})],
}],
entry_point: "main".to_string(),
};
let ir = from_ast(&ast).expect("Nested return __if_expr in then should work");
assert_ir_is_sequence(&ir);
}
#[test]
fn test_lower_return_if_expr_nested_else() {
let ast = RestrictedAst {
functions: vec![Function {
name: "get_val".to_string(),
params: vec![],
return_type: Type::U32,
body: vec![Stmt::Return(Some(Expr::FunctionCall {
name: "__if_expr".to_string(),
args: vec![
Expr::Literal(Literal::Bool(true)),
Expr::Literal(Literal::U32(1)),
Expr::FunctionCall {
name: "__if_expr".to_string(),
args: vec![
Expr::Literal(Literal::Bool(false)),
Expr::Literal(Literal::U32(2)),
Expr::Literal(Literal::U32(3)),
],
},
],
}))],
},
Function {
name: "main".to_string(),
params: vec![],
return_type: Type::Void,
body: vec![Stmt::Expr(Expr::FunctionCall {
name: "get_val".to_string(),
args: vec![],
})],
}],
entry_point: "main".to_string(),
};
let ir = from_ast(&ast).expect("Nested return __if_expr in else (elif) should work");
assert_ir_is_sequence(&ir);
}