#![allow(clippy::missing_errors_doc)]
use crate::frontend::ast::{BinaryOp, Expr, Literal, Pattern, UnaryOp};
use anyhow::Result;
pub struct MinimalCodeGen;
impl MinimalCodeGen {
pub fn gen_expr(expr: &Expr) -> Result<String> {
use crate::frontend::ast::ExprKind;
match &expr.kind {
ExprKind::Literal(lit) => Self::gen_literal(lit),
ExprKind::Identifier(name) => Ok(name.clone()),
ExprKind::Binary { left, op, right } => Self::gen_binary_expr(left, *op, right),
ExprKind::Unary { op, operand } => Self::gen_unary_expr(*op, operand),
ExprKind::Let { name, value, body, .. } => Self::gen_let_expr(name, value, body),
ExprKind::Function { name, params, body, .. } => Self::gen_function_expr(name, params, body),
ExprKind::Lambda { params, body } => Self::gen_lambda_expr(params, body),
ExprKind::Call { func, args } => Self::gen_call_expr(func, args),
ExprKind::If {
condition,
then_branch,
else_branch,
} => Self::gen_if_expr(condition, then_branch, else_branch.as_deref()),
ExprKind::Ternary {
condition,
true_expr,
false_expr,
} =>
{
Self::gen_if_expr(condition, true_expr, Some(false_expr))
}
ExprKind::Block(exprs) => Self::gen_block_expr(exprs),
ExprKind::Match { expr, arms } => Self::gen_match_expr(expr, arms),
ExprKind::List(elements) => Self::gen_list_expr(elements),
ExprKind::Struct { name, fields, .. } => Self::gen_struct_def(name, fields),
ExprKind::StructLiteral { name, fields, base: _ } => Self::gen_struct_literal(name, fields),
ExprKind::MethodCall { receiver, method, args } => Self::gen_method_call(receiver, method, args),
ExprKind::Macro { name, args } => Self::gen_macro_call(name, args),
ExprKind::QualifiedName { module, name } => Ok(format!("{module}::{name}")),
ExprKind::StringInterpolation { parts } => Self::gen_string_interpolation(parts),
_ => Err(anyhow::anyhow!(
"Minimal codegen does not support {:?} - use full transpiler for complete language support",
expr.kind
)),
}
}
fn gen_binary_expr(left: &Expr, op: BinaryOp, right: &Expr) -> Result<String> {
let left_code = Self::gen_expr(left)?;
let right_code = Self::gen_expr(right)?;
let op_code = Self::gen_binary_op(op);
Ok(format!("({left_code} {op_code} {right_code})"))
}
fn gen_unary_expr(op: UnaryOp, operand: &Expr) -> Result<String> {
let operand_code = Self::gen_expr(operand)?;
let op_code = Self::gen_unary_op(op);
Ok(format!("({op_code} {operand_code})"))
}
fn gen_let_expr(name: &str, value: &Expr, body: &Expr) -> Result<String> {
let value_code = Self::gen_expr(value)?;
let body_code = Self::gen_expr(body)?;
Ok(format!("{{ let {name} = {value_code}; {body_code} }}"))
}
fn gen_function_expr(
name: &str,
params: &[crate::frontend::ast::Param],
body: &Expr,
) -> Result<String> {
let param_list = params
.iter()
.map(|p| {
let name = p.name();
format!("{name}: i32")
}) .collect::<Vec<_>>()
.join(", ");
let body_code = Self::gen_expr(body)?;
Ok(format!("fn {name}({param_list}) {{ {body_code} }}"))
}
fn gen_lambda_expr(params: &[crate::frontend::ast::Param], body: &Expr) -> Result<String> {
let param_list = params
.iter()
.map(crate::frontend::ast::Param::name)
.collect::<Vec<_>>()
.join(", ");
let body_code = Self::gen_expr(body)?;
Ok(format!("|{param_list}| {body_code}"))
}
fn gen_call_expr(func: &Expr, args: &[Expr]) -> Result<String> {
let func_code = Self::gen_expr(func)?;
let arg_codes = args
.iter()
.map(Self::gen_expr)
.collect::<Result<Vec<_>>>()?;
Ok(format!("{func_code}({})", arg_codes.join(", ")))
}
fn gen_if_expr(
condition: &Expr,
then_branch: &Expr,
else_branch: Option<&Expr>,
) -> Result<String> {
let cond_code = Self::gen_expr(condition)?;
let then_code = Self::gen_expr(then_branch)?;
if let Some(else_expr) = else_branch {
let else_code = Self::gen_expr(else_expr)?;
Ok(format!(
"if {cond_code} {{ {then_code} }} else {{ {else_code} }}"
))
} else {
Ok(format!("if {cond_code} {{ {then_code} }}"))
}
}
fn gen_block_expr(exprs: &[Expr]) -> Result<String> {
let mut code = String::new();
code.push_str("{ ");
for (i, expr) in exprs.iter().enumerate() {
let expr_code = Self::gen_expr(expr)?;
if i == exprs.len() - 1 {
code.push_str(&expr_code);
} else {
code.push_str(&format!("{expr_code}; "));
}
}
code.push_str(" }");
Ok(code)
}
fn gen_match_expr(expr: &Expr, arms: &[crate::frontend::ast::MatchArm]) -> Result<String> {
let expr_code = Self::gen_expr(expr)?;
let mut code = format!("match {expr_code} {{\n");
for arm in arms {
let pattern_code = Self::gen_pattern(&arm.pattern)?;
let body_code = Self::gen_expr(&arm.body)?;
code.push_str(&format!(" {pattern_code} => {body_code},\n"));
}
code.push('}');
Ok(code)
}
fn gen_list_expr(elements: &[Expr]) -> Result<String> {
let element_codes = elements
.iter()
.map(Self::gen_expr)
.collect::<Result<Vec<_>>>()?;
let elements = element_codes.join(", ");
Ok(format!("vec![{elements}]"))
}
fn gen_struct_def(name: &str, fields: &[crate::frontend::ast::StructField]) -> Result<String> {
let field_list = fields
.iter()
.map(|f| {
let name = &f.name;
format!(" {name}: String,")
}) .collect::<Vec<_>>()
.join("\n");
Ok(format!("struct {name} {{\n{field_list}\n}}"))
}
fn gen_struct_literal(name: &str, fields: &[(String, Expr)]) -> Result<String> {
let field_codes = fields
.iter()
.map(|f| {
let value_code = Self::gen_expr(&f.1)?;
let field_name = &f.0;
Ok(format!("{field_name}: {value_code}"))
})
.collect::<Result<Vec<_>>>()?;
let fields = field_codes.join(", ");
Ok(format!("{name} {{ {fields} }}"))
}
fn gen_method_call(receiver: &Expr, method: &str, args: &[Expr]) -> Result<String> {
let receiver_code = Self::gen_expr(receiver)?;
let arg_codes = args
.iter()
.map(Self::gen_expr)
.collect::<Result<Vec<_>>>()?;
let args = arg_codes.join(", ");
Ok(format!("{receiver_code}.{method}({args})"))
}
fn gen_macro_call(name: &str, args: &[Expr]) -> Result<String> {
let arg_codes = args
.iter()
.map(Self::gen_expr)
.collect::<Result<Vec<_>>>()?;
let args = arg_codes.join(", ");
Ok(format!("{name}!({args})"))
}
fn gen_string_interpolation(parts: &[crate::frontend::ast::StringPart]) -> Result<String> {
let mut result = String::from("format!(");
let mut format_str = String::new();
let mut args = Vec::new();
for part in parts {
match part {
crate::frontend::ast::StringPart::Text(s) => {
format_str.push_str(s);
}
crate::frontend::ast::StringPart::Expr(expr) => {
format_str.push_str("{}");
args.push(Self::gen_expr(expr)?);
}
crate::frontend::ast::StringPart::ExprWithFormat { expr, format_spec } => {
format_str.push('{');
format_str.push_str(format_spec);
format_str.push('}');
args.push(Self::gen_expr(expr)?);
}
}
}
result.push_str(&format!("\"{format_str}\""));
if !args.is_empty() {
result.push_str(", ");
result.push_str(&args.join(", "));
}
result.push(')');
Ok(result)
}
fn gen_literal(lit: &Literal) -> Result<String> {
match lit {
Literal::Integer(i, _) => Ok(i.to_string()),
Literal::Float(f) => Ok(f.to_string()),
Literal::String(s) => Ok(format!("\"{}\"", s.replace('"', "\\\""))),
Literal::Bool(b) => Ok(b.to_string()),
Literal::Char(c) => Ok(format!("'{c}'")),
Literal::Byte(b) => Ok(format!("b'{}'", *b as char)),
Literal::Unit => Ok("()".to_string()),
Literal::Null => Ok("None".to_string()),
Literal::Atom(s) => Ok(format!(":{s}")),
}
}
fn gen_binary_op(op: BinaryOp) -> &'static str {
match op {
BinaryOp::Add => "+",
BinaryOp::Subtract => "-",
BinaryOp::Multiply => "*",
BinaryOp::Divide => "/",
BinaryOp::Modulo => "%",
BinaryOp::Equal => "==",
BinaryOp::NotEqual => "!=",
BinaryOp::Less => "<",
BinaryOp::LessEqual => "<=",
BinaryOp::Greater => ">",
BinaryOp::Gt => ">", BinaryOp::GreaterEqual => ">=",
BinaryOp::And => "&&",
BinaryOp::Or => "||",
BinaryOp::NullCoalesce => "??", BinaryOp::BitwiseAnd => "&",
BinaryOp::BitwiseOr => "|",
BinaryOp::BitwiseXor => "^",
BinaryOp::LeftShift => "<<",
BinaryOp::RightShift => ">>",
BinaryOp::Power => "pow", BinaryOp::Send => "!", BinaryOp::In => "in", }
}
fn gen_unary_op(op: UnaryOp) -> &'static str {
match op {
UnaryOp::Not => "!",
UnaryOp::Negate => "-",
UnaryOp::BitwiseNot => "~",
UnaryOp::Reference => "&",
UnaryOp::MutableReference => "&mut ", UnaryOp::Deref => "*",
}
}
fn gen_pattern(pattern: &Pattern) -> Result<String> {
match pattern {
Pattern::Wildcard => Ok("_".to_string()),
Pattern::Literal(lit) => Self::gen_literal(lit),
Pattern::Identifier(name) => Ok(name.clone()),
Pattern::List(patterns) => {
let pattern_codes = patterns
.iter()
.map(Self::gen_pattern)
.collect::<Result<Vec<_>>>()?;
let patterns = pattern_codes.join(", ");
Ok(format!("[{patterns}]"))
}
Pattern::Ok(inner) => {
let inner_code = Self::gen_pattern(inner)?;
Ok(format!("Ok({inner_code})"))
}
Pattern::Err(inner) => {
let inner_code = Self::gen_pattern(inner)?;
Ok(format!("Err({inner_code})"))
}
Pattern::Some(inner) => {
let inner_code = Self::gen_pattern(inner)?;
Ok(format!("Some({inner_code})"))
}
Pattern::None => Ok("None".to_string()),
_ => Ok("_".to_string()), }
}
#[allow(dead_code)]
fn gen_type(_ty: &crate::frontend::ast::Type) -> Result<String> {
Ok("String".to_string()) }
pub fn gen_program(expr: &Expr) -> Result<String> {
let main_code = Self::gen_expr(expr)?;
Ok(format!("use std::collections::HashMap;\n\n{main_code}"))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::frontend::ast::{BinaryOp, Expr, ExprKind, Literal, Pattern, Span, UnaryOp};
use crate::frontend::parser::Parser;
fn gen_str(input: &str) -> Result<String> {
let mut parser = Parser::new(input);
let expr = parser.parse()?;
MinimalCodeGen::gen_expr(&expr)
}
fn make_literal(lit: Literal) -> Expr {
Expr::new(ExprKind::Literal(lit), Span::new(0, 1))
}
fn make_ident(name: &str) -> Expr {
Expr::new(ExprKind::Identifier(name.to_string()), Span::new(0, 1))
}
#[test]
fn test_basic_expressions() {
assert_eq!(
gen_str("42").expect("operation should succeed in test"),
"42"
);
assert_eq!(
gen_str("true").expect("operation should succeed in test"),
"true"
);
assert_eq!(
gen_str("\"hello\"").expect("operation should succeed in test"),
"\"hello\""
);
}
#[test]
#[allow(clippy::approx_constant)]
fn test_all_literals() {
assert_eq!(
MinimalCodeGen::gen_literal(&Literal::Integer(42, None))
.expect("operation should succeed in test"),
"42"
);
assert_eq!(
MinimalCodeGen::gen_literal(&Literal::Float(3.15159))
.expect("operation should succeed in test"),
"3.15159"
);
assert_eq!(
MinimalCodeGen::gen_literal(&Literal::String("test".into()))
.expect("operation should succeed in test"),
"\"test\""
);
assert_eq!(
MinimalCodeGen::gen_literal(&Literal::Bool(true))
.expect("operation should succeed in test"),
"true"
);
assert_eq!(
MinimalCodeGen::gen_literal(&Literal::Bool(false))
.expect("operation should succeed in test"),
"false"
);
assert_eq!(
MinimalCodeGen::gen_literal(&Literal::Char('a'))
.expect("operation should succeed in test"),
"'a'"
);
assert_eq!(
MinimalCodeGen::gen_literal(&Literal::Unit).expect("operation should succeed in test"),
"()"
);
}
#[test]
fn test_string_escaping() {
let lit = Literal::String("Hello \"World\"".into());
assert_eq!(
MinimalCodeGen::gen_literal(&lit).expect("operation should succeed in test"),
"\"Hello \\\"World\\\"\""
);
}
#[test]
fn test_binary_ops() {
assert_eq!(
gen_str("1 + 2").expect("operation should succeed in test"),
"(1 + 2)"
);
assert_eq!(
gen_str("x * y").expect("operation should succeed in test"),
"(x * y)"
);
}
#[test]
fn test_all_binary_operators() {
assert_eq!(MinimalCodeGen::gen_binary_op(BinaryOp::Add), "+");
assert_eq!(MinimalCodeGen::gen_binary_op(BinaryOp::Subtract), "-");
assert_eq!(MinimalCodeGen::gen_binary_op(BinaryOp::Multiply), "*");
assert_eq!(MinimalCodeGen::gen_binary_op(BinaryOp::Divide), "/");
assert_eq!(MinimalCodeGen::gen_binary_op(BinaryOp::Modulo), "%");
assert_eq!(MinimalCodeGen::gen_binary_op(BinaryOp::Equal), "==");
assert_eq!(MinimalCodeGen::gen_binary_op(BinaryOp::NotEqual), "!=");
assert_eq!(MinimalCodeGen::gen_binary_op(BinaryOp::Less), "<");
assert_eq!(MinimalCodeGen::gen_binary_op(BinaryOp::LessEqual), "<=");
assert_eq!(MinimalCodeGen::gen_binary_op(BinaryOp::Greater), ">");
assert_eq!(MinimalCodeGen::gen_binary_op(BinaryOp::GreaterEqual), ">=");
assert_eq!(MinimalCodeGen::gen_binary_op(BinaryOp::And), "&&");
assert_eq!(MinimalCodeGen::gen_binary_op(BinaryOp::Or), "||");
assert_eq!(MinimalCodeGen::gen_binary_op(BinaryOp::NullCoalesce), "??");
assert_eq!(MinimalCodeGen::gen_binary_op(BinaryOp::BitwiseAnd), "&");
assert_eq!(MinimalCodeGen::gen_binary_op(BinaryOp::BitwiseOr), "|");
assert_eq!(MinimalCodeGen::gen_binary_op(BinaryOp::BitwiseXor), "^");
assert_eq!(MinimalCodeGen::gen_binary_op(BinaryOp::LeftShift), "<<");
assert_eq!(MinimalCodeGen::gen_binary_op(BinaryOp::Power), "pow");
}
#[test]
fn test_all_unary_operators() {
assert_eq!(MinimalCodeGen::gen_unary_op(UnaryOp::Not), "!");
assert_eq!(MinimalCodeGen::gen_unary_op(UnaryOp::Negate), "-");
assert_eq!(MinimalCodeGen::gen_unary_op(UnaryOp::BitwiseNot), "~");
assert_eq!(MinimalCodeGen::gen_unary_op(UnaryOp::Reference), "&");
}
#[test]
fn test_unary_expressions() {
assert_eq!(
gen_str("!true").expect("operation should succeed in test"),
"(! true)"
);
assert_eq!(
gen_str("-42").expect("operation should succeed in test"),
"(- 42)"
);
}
#[test]
fn test_if_expression() {
assert_eq!(
gen_str("if true { 1 }").expect("operation should succeed in test"),
"if true { { 1 } }"
);
assert_eq!(
gen_str("if x > 0 { 1 } else { 2 }").expect("operation should succeed in test"),
"if (x > 0) { { 1 } } else { { 2 } }"
);
}
#[test]
fn test_block_expression() {
assert_eq!(
gen_str("{ x }").expect("operation should succeed in test"),
"{ x }"
);
assert_eq!(
gen_str("{ x; y }").expect("operation should succeed in test"),
"{ x; y }"
);
assert_eq!(
gen_str("{ x; y; z }").expect("operation should succeed in test"),
"{ x; y; z }"
);
}
#[test]
fn test_empty_block() {
let block = Expr::new(ExprKind::Block(vec![]), Span::new(0, 1));
assert_eq!(
MinimalCodeGen::gen_expr(&block).expect("operation should succeed in test"),
"{ }"
);
}
#[test]
fn test_function_def() {
let result = gen_str("fun add(x: i32, y: i32) -> i32 { x + y }")
.expect("operation should succeed in test");
assert!(result.contains("fn add(x: i32, y: i32)"));
}
#[test]
fn test_lambda() {
assert_eq!(
gen_str("|x| x + 1").expect("operation should succeed in test"),
"|x| (x + 1)"
);
}
#[test]
fn test_lambda_multiple_params() {
assert_eq!(
gen_str("|x, y| x + y").expect("operation should succeed in test"),
"|x, y| (x + y)"
);
}
#[test]
fn test_function_call() {
assert_eq!(
gen_str("foo()").expect("operation should succeed in test"),
"foo()"
);
assert_eq!(
gen_str("add(1, 2)").expect("operation should succeed in test"),
"add(1, 2)"
);
assert_eq!(
gen_str("sum(a, b, c)").expect("operation should succeed in test"),
"sum(a, b, c)"
);
}
#[test]
fn test_method_call() {
assert_eq!(
gen_str("obj.method()").expect("operation should succeed in test"),
"obj.method()"
);
assert_eq!(
gen_str("str.len()").expect("operation should succeed in test"),
"str.len()"
);
assert_eq!(
gen_str("list.push(42)").expect("operation should succeed in test"),
"list.push(42)"
);
}
#[test]
fn test_list() {
assert_eq!(
gen_str("[1, 2, 3]").expect("operation should succeed in test"),
"vec![1, 2, 3]"
);
}
#[test]
fn test_empty_list() {
assert_eq!(
gen_str("[]").expect("operation should succeed in test"),
"vec![]"
);
}
#[test]
fn test_nested_list() {
assert_eq!(
gen_str("[[1, 2], [3, 4]]").expect("operation should succeed in test"),
"vec![vec![1, 2], vec![3, 4]]"
);
}
#[test]
fn test_macro_call() {
assert_eq!(
gen_str("println(\"hello\")").expect("operation should succeed in test"),
"println(\"hello\")"
);
assert_eq!(
gen_str("[1, 2]").expect("operation should succeed in test"),
"vec![1, 2]"
);
}
#[test]
fn test_qualified_name() {
let expr = Expr::new(
ExprKind::QualifiedName {
module: "std".to_string(),
name: "println".to_string(),
},
Span::new(0, 1),
);
assert_eq!(
MinimalCodeGen::gen_expr(&expr).expect("operation should succeed in test"),
"std::println"
);
}
#[test]
fn test_pattern_wildcard() {
assert_eq!(
MinimalCodeGen::gen_pattern(&Pattern::Wildcard)
.expect("operation should succeed in test"),
"_"
);
}
#[test]
fn test_pattern_literal() {
let pat = Pattern::Literal(Literal::Integer(42, None));
assert_eq!(
MinimalCodeGen::gen_pattern(&pat).expect("operation should succeed in test"),
"42"
);
}
#[test]
fn test_pattern_identifier() {
let pat = Pattern::Identifier("x".to_string());
assert_eq!(
MinimalCodeGen::gen_pattern(&pat).expect("operation should succeed in test"),
"x"
);
}
#[test]
fn test_pattern_list() {
let patterns = vec![
Pattern::Literal(Literal::Integer(1, None)),
Pattern::Identifier("x".to_string()),
Pattern::Wildcard,
];
let pat = Pattern::List(patterns);
assert_eq!(
MinimalCodeGen::gen_pattern(&pat).expect("operation should succeed in test"),
"[1, x, _]"
);
}
#[test]
fn test_pattern_result_ok() {
let inner = Box::new(Pattern::Identifier("x".to_string()));
let pat = Pattern::Ok(inner);
assert_eq!(
MinimalCodeGen::gen_pattern(&pat).expect("operation should succeed in test"),
"Ok(x)"
);
}
#[test]
fn test_pattern_result_err() {
let inner = Box::new(Pattern::Identifier("e".to_string()));
let pat = Pattern::Err(inner);
assert_eq!(
MinimalCodeGen::gen_pattern(&pat).expect("operation should succeed in test"),
"Err(e)"
);
}
#[test]
fn test_pattern_option_some() {
let inner = Box::new(Pattern::Literal(Literal::Integer(42, None)));
let pat = Pattern::Some(inner);
assert_eq!(
MinimalCodeGen::gen_pattern(&pat).expect("operation should succeed in test"),
"Some(42)"
);
}
#[test]
fn test_pattern_option_none() {
assert_eq!(
MinimalCodeGen::gen_pattern(&Pattern::None).expect("operation should succeed in test"),
"None"
);
}
#[test]
fn test_gen_program() {
let expr = make_literal(Literal::Integer(42, None));
let result = MinimalCodeGen::gen_program(&expr).expect("operation should succeed in test");
assert!(result.starts_with("use std::collections::HashMap;"));
assert!(result.contains("42"));
}
#[test]
fn test_complex_expression() {
let result =
gen_str("if x > 0 { x * 2 } else { -x }").expect("operation should succeed in test");
assert!(result.contains("if"));
assert!(result.contains("else"));
}
#[test]
fn test_nested_if() {
let input = "if a { if b { 1 } else { 2 } } else { 3 }";
let result = gen_str(input).expect("operation should succeed in test");
assert!(result.contains("if a"));
assert!(result.contains("if b"));
}
#[test]
fn test_chained_method_calls() {
assert_eq!(
gen_str("obj.method1().method2()").expect("operation should succeed in test"),
"obj.method1().method2()"
);
}
#[test]
fn test_struct_literal_empty() {
let fields = vec![];
let result = MinimalCodeGen::gen_struct_literal("Point", &fields)
.expect("operation should succeed in test");
assert_eq!(result, "Point { }");
}
#[test]
fn test_struct_literal_with_fields() {
let fields = vec![
("x".to_string(), make_literal(Literal::Integer(10, None))),
("y".to_string(), make_literal(Literal::Integer(20, None))),
];
let result = MinimalCodeGen::gen_struct_literal("Point", &fields)
.expect("operation should succeed in test");
assert_eq!(result, "Point { x: 10, y: 20 }");
}
#[test]
fn test_string_interpolation_simple() {
use crate::frontend::ast::StringPart;
let parts = vec![
StringPart::Text("Hello, ".to_string()),
StringPart::Expr(Box::new(make_ident("name"))),
StringPart::Text("!".to_string()),
];
let result = MinimalCodeGen::gen_string_interpolation(&parts)
.expect("operation should succeed in test");
assert_eq!(result, r#"format!("Hello, {}!", name)"#);
}
#[test]
fn test_string_interpolation_with_format() {
use crate::frontend::ast::StringPart;
let parts = vec![
StringPart::Text("Value: ".to_string()),
StringPart::ExprWithFormat {
expr: Box::new(make_ident("x")),
format_spec: ":.2".to_string(),
},
];
let result = MinimalCodeGen::gen_string_interpolation(&parts)
.expect("operation should succeed in test");
assert_eq!(result, r#"format!("Value: {:.2}", x)"#);
}
#[test]
fn test_string_interpolation_empty() {
let parts = vec![];
let result = MinimalCodeGen::gen_string_interpolation(&parts)
.expect("operation should succeed in test");
assert_eq!(result, r#"format!("")"#);
}
#[test]
fn test_let_expression() {
assert_eq!(
gen_str("let x = 5 in x + 1").expect("operation should succeed in test"),
"{ let x = 5; (x + 1) }"
);
}
#[test]
fn test_match_expression_simple() {
let input = "match x { 1 => \"one\", 2 => \"two\", _ => \"other\" }";
let result = gen_str(input);
if let Ok(code) = result {
assert!(code.contains("match x {"));
assert!(code.contains("1 => \"one\""));
assert!(code.contains("2 => \"two\""));
assert!(code.contains("_ => \"other\""));
}
}
#[test]
fn test_struct_definition() {
let input = "struct Point { x: i32, y: i32 }";
let result = gen_str(input);
if let Ok(code) = result {
assert!(code.contains("struct Point"));
assert!(code.contains("x: String"));
assert!(code.contains("y: String"));
}
}
#[test]
fn test_binary_precedence() {
assert_eq!(
gen_str("a + b * c").expect("operation should succeed in test"),
"(a + (b * c))"
);
assert_eq!(
gen_str("(a + b) * c").expect("operation should succeed in test"),
"((a + b) * c)"
);
}
#[test]
fn test_deeply_nested_expression() {
let input = "((a + b) * (c - d)) / (e + f)";
let result = gen_str(input).expect("operation should succeed in test");
assert!(result.contains('+'));
assert!(result.contains('-'));
assert!(result.contains('*'));
assert!(result.contains('/'));
}
#[test]
fn test_gen_literal_null() {
let result =
MinimalCodeGen::gen_literal(&Literal::Null).expect("operation should succeed in test");
assert_eq!(result, "None");
}
#[test]
fn test_gen_literal_byte() {
let result = MinimalCodeGen::gen_literal(&Literal::Byte(65))
.expect("operation should succeed in test");
assert_eq!(result, "b'A'");
}
#[test]
fn test_gen_literal_byte_newline() {
let result = MinimalCodeGen::gen_literal(&Literal::Byte(10))
.expect("operation should succeed in test");
assert_eq!(result, "b'\n'");
}
#[test]
fn test_gen_binary_op_power() {
assert_eq!(MinimalCodeGen::gen_binary_op(BinaryOp::Power), "pow");
}
#[test]
fn test_gen_binary_op_send() {
assert_eq!(MinimalCodeGen::gen_binary_op(BinaryOp::Send), "!");
}
#[test]
fn test_gen_binary_op_right_shift() {
assert_eq!(MinimalCodeGen::gen_binary_op(BinaryOp::RightShift), ">>");
}
#[test]
fn test_gen_unary_op_mut_ref() {
assert_eq!(
MinimalCodeGen::gen_unary_op(UnaryOp::MutableReference),
"&mut "
);
}
#[test]
fn test_gen_unary_op_deref() {
assert_eq!(MinimalCodeGen::gen_unary_op(UnaryOp::Deref), "*");
}
#[test]
fn test_gen_expr_unsupported_error() {
use crate::frontend::ast::ExprKind;
let expr = Expr::new(
ExprKind::Return {
value: Some(Box::new(make_literal(Literal::Integer(42, None)))),
},
Span::new(0, 1),
);
let result = MinimalCodeGen::gen_expr(&expr);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("Minimal codegen does not support"));
}
#[test]
fn test_gen_expr_ternary() {
let condition = make_ident("x");
let true_expr = make_literal(Literal::Integer(1, None));
let false_expr = make_literal(Literal::Integer(2, None));
let expr = Expr::new(
ExprKind::Ternary {
condition: Box::new(condition),
true_expr: Box::new(true_expr),
false_expr: Box::new(false_expr),
},
Span::new(0, 1),
);
let result = MinimalCodeGen::gen_expr(&expr).expect("operation should succeed in test");
assert!(result.contains("if x"));
assert!(result.contains("{ 1 }"));
assert!(result.contains("else"));
assert!(result.contains("{ 2 }"));
}
#[test]
fn test_gen_type_simplified() {
use crate::frontend::ast::{Type, TypeKind};
let ty = Type {
kind: TypeKind::Named("i32".to_string()),
span: Span::new(0, 1),
};
let result = MinimalCodeGen::gen_type(&ty).expect("operation should succeed in test");
assert_eq!(result, "String"); }
#[test]
fn test_gen_function_expr_no_params() {
let body = make_literal(Literal::Integer(42, None));
let result = MinimalCodeGen::gen_function_expr("foo", &[], &body)
.expect("operation should succeed in test");
assert_eq!(result, "fn foo() { 42 }");
}
#[test]
fn test_gen_lambda_expr_no_params() {
let body = make_literal(Literal::Integer(42, None));
let result =
MinimalCodeGen::gen_lambda_expr(&[], &body).expect("operation should succeed in test");
assert_eq!(result, "|| 42");
}
#[test]
fn test_gen_method_call_no_args() {
let receiver = make_ident("obj");
let result = MinimalCodeGen::gen_method_call(&receiver, "method", &[])
.expect("operation should succeed in test");
assert_eq!(result, "obj.method()");
}
#[test]
fn test_gen_block_expr_single() {
let exprs = vec![make_literal(Literal::Integer(42, None))];
let result =
MinimalCodeGen::gen_block_expr(&exprs).expect("operation should succeed in test");
assert_eq!(result, "{ 42 }");
}
}
#[cfg(test)]
mod property_tests_codegen_minimal {
use proptest::proptest;
proptest! {
#[test]
fn test_gen_expr_never_panics(input: String) {
let _input = if input.len() > 100 { &input[..100] } else { &input[..] };
let _ = std::panic::catch_unwind(|| {
});
}
}
}