Coverage Report

Created: 2025-09-08 21:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/home/noah/src/ruchy/src/backend/transpiler/codegen_minimal.rs
Line
Count
Source
1
//! Minimal codegen for self-hosting MVP
2
//! Direct Rust mapping with no optimization - as specified in self-hosting spec
3
4
#![allow(clippy::missing_errors_doc)]
5
6
use crate::frontend::ast::{Expr, ExprKind, Literal, Pattern, BinaryOp, UnaryOp};
7
use anyhow::Result;
8
9
/// Minimal code generator for self-hosting
10
pub struct MinimalCodeGen;
11
12
impl MinimalCodeGen {
13
    /// Generate Rust code directly from AST with no optimization
14
0
    pub fn gen_expr(expr: &Expr) -> Result<String> {
15
0
        match &expr.kind {
16
0
            ExprKind::Literal(lit) => Self::gen_literal(lit),
17
            
18
0
            ExprKind::Identifier(name) => Ok(name.clone()),
19
            
20
0
            ExprKind::Binary { left, op, right } => {
21
0
                let left_code = Self::gen_expr(left)?;
22
0
                let right_code = Self::gen_expr(right)?;
23
0
                let op_code = Self::gen_binary_op(*op);
24
0
                Ok(format!("({left_code} {op_code} {right_code})"))
25
            }
26
            
27
0
            ExprKind::Unary { op, operand } => {
28
0
                let operand_code = Self::gen_expr(operand)?;
29
0
                let op_code = Self::gen_unary_op(*op);
30
0
                Ok(format!("({op_code} {operand_code})"))
31
            }
32
            
33
0
            ExprKind::Let { name, value, body, .. } => {
34
0
                let value_code = Self::gen_expr(value)?;
35
0
                let body_code = Self::gen_expr(body)?;
36
0
                Ok(format!("{{ let {name} = {value_code}; {body_code} }}"))
37
            }
38
            
39
0
            ExprKind::Function { name, params, body, .. } => {
40
0
                let param_list = params.iter()
41
0
                    .map(|p| { let name = p.name(); format!("{name}: i32") }) // Simplified for MVP
42
0
                    .collect::<Vec<_>>()
43
0
                    .join(", ");
44
0
                let body_code = Self::gen_expr(body)?;
45
0
                Ok(format!("fn {name}({param_list}) {{ {body_code} }}"))
46
            }
47
            
48
0
            ExprKind::Lambda { params, body } => {
49
0
                let param_list = params.iter()
50
0
                    .map(crate::frontend::ast::Param::name)
51
0
                    .collect::<Vec<_>>()
52
0
                    .join(", ");
53
0
                let body_code = Self::gen_expr(body)?;
54
0
                Ok(format!("|{param_list}| {body_code}"))
55
            }
56
            
57
0
            ExprKind::Call { func, args } => {
58
0
                let func_code = Self::gen_expr(func)?;
59
0
                let arg_codes = args.iter()
60
0
                    .map(Self::gen_expr)
61
0
                    .collect::<Result<Vec<_>>>()?;
62
0
                Ok(format!("{func_code}({})", arg_codes.join(", ")))
63
            }
64
            
65
0
            ExprKind::If { condition, then_branch, else_branch } => {
66
0
                let cond_code = Self::gen_expr(condition)?;
67
0
                let then_code = Self::gen_expr(then_branch)?;
68
0
                if let Some(else_expr) = else_branch {
69
0
                    let else_code = Self::gen_expr(else_expr)?;
70
0
                    Ok(format!("if {cond_code} {{ {then_code} }} else {{ {else_code} }}"))
71
                } else {
72
0
                    Ok(format!("if {cond_code} {{ {then_code} }}"))
73
                }
74
            }
75
            
76
0
            ExprKind::Block(exprs) => {
77
0
                let mut code = String::new();
78
0
                code.push_str("{ ");
79
0
                for (i, expr) in exprs.iter().enumerate() {
80
0
                    let expr_code = Self::gen_expr(expr)?;
81
0
                    if i == exprs.len() - 1 {
82
0
                        // Last expression is the return value
83
0
                        code.push_str(&expr_code);
84
0
                    } else {
85
0
                        code.push_str(&format!("{expr_code}; "));
86
0
                    }
87
                }
88
0
                code.push_str(" }");
89
0
                Ok(code)
90
            }
91
            
92
0
            ExprKind::Match { expr, arms } => {
93
0
                let expr_code = Self::gen_expr(expr)?;
94
0
                let mut code = format!("match {expr_code} {{\n");
95
0
                for arm in arms {
96
0
                    let pattern_code = Self::gen_pattern(&arm.pattern)?;
97
0
                    let body_code = Self::gen_expr(&arm.body)?;
98
0
                    code.push_str(&format!("    {pattern_code} => {body_code},\n"));
99
                }
100
0
                code.push('}');
101
0
                Ok(code)
102
            }
103
            
104
0
            ExprKind::List(elements) => {
105
0
                let element_codes = elements.iter()
106
0
                    .map(Self::gen_expr)
107
0
                    .collect::<Result<Vec<_>>>()?;
108
0
                let elements = element_codes.join(", ");
109
0
                Ok(format!("vec![{elements}]"))
110
            }
111
            
112
0
            ExprKind::Struct { name, fields, .. } => {
113
0
                let field_list = fields.iter()
114
0
                    .map(|f| { let name = &f.name; format!("    {name}: String,") }) // Simplified for MVP
115
0
                    .collect::<Vec<_>>()
116
0
                    .join("\n");
117
0
                Ok(format!("struct {name} {{\n{field_list}\n}}"))
118
            }
119
            
120
0
            ExprKind::StructLiteral { name, fields } => {
121
0
                let field_codes = fields.iter()
122
0
                    .map(|f| {
123
0
                        let value_code = Self::gen_expr(&f.1)?;
124
0
                        let field_name = &f.0;
125
0
                        Ok(format!("{field_name}: {value_code}"))
126
0
                    })
127
0
                    .collect::<Result<Vec<_>>>()?;
128
0
                let fields = field_codes.join(", ");
129
0
                Ok(format!("{name} {{ {fields} }}"))
130
            }
131
            
132
0
            ExprKind::MethodCall { receiver, method, args } => {
133
0
                let receiver_code = Self::gen_expr(receiver)?;
134
0
                let arg_codes = args.iter()
135
0
                    .map(Self::gen_expr)
136
0
                    .collect::<Result<Vec<_>>>()?;
137
0
                let args = arg_codes.join(", ");
138
0
                Ok(format!("{receiver_code}.{method}({args})"))
139
            }
140
            
141
0
            ExprKind::Macro { name, args } => {
142
0
                let arg_codes = args.iter()
143
0
                    .map(Self::gen_expr)
144
0
                    .collect::<Result<Vec<_>>>()?;
145
0
                let args = arg_codes.join(", ");
146
0
                Ok(format!("{name}!({args})"))
147
            }
148
            
149
0
            ExprKind::QualifiedName { module, name } => {
150
0
                Ok(format!("{module}::{name}"))
151
            }
152
            
153
0
            ExprKind::StringInterpolation { parts } => {
154
                // Simplified string interpolation for MVP
155
0
                let mut result = String::from("format!(");
156
0
                let mut format_str = String::new();
157
0
                let mut args = Vec::new();
158
                
159
0
                for part in parts {
160
0
                    match part {
161
0
                        crate::frontend::ast::StringPart::Text(s) => {
162
0
                            format_str.push_str(s);
163
0
                        }
164
0
                        crate::frontend::ast::StringPart::Expr(expr) => {
165
0
                            format_str.push_str("{}");
166
0
                            args.push(Self::gen_expr(expr)?);
167
                        }
168
0
                        crate::frontend::ast::StringPart::ExprWithFormat { expr, format_spec } => {
169
0
                            format_str.push('{');
170
0
                            format_str.push_str(format_spec);
171
0
                            format_str.push('}');
172
0
                            args.push(Self::gen_expr(expr)?);
173
                        }
174
                    }
175
                }
176
                
177
0
                result.push_str(&format!("\"{format_str}\""));
178
0
                if !args.is_empty() {
179
0
                    result.push_str(", ");
180
0
                    result.push_str(&args.join(", "));
181
0
                }
182
0
                result.push(')');
183
0
                Ok(result)
184
            }
185
            
186
            _ => {
187
                // For self-hosting MVP, unsupported constructs generate compile errors
188
                // This ensures developers know what needs to be implemented
189
0
                Err(anyhow::anyhow!(
190
0
                    "Minimal codegen does not support {:?} - use full transpiler for complete language support", 
191
0
                    expr.kind
192
0
                ))
193
            }
194
        }
195
0
    }
196
    
197
0
    fn gen_literal(lit: &Literal) -> Result<String> {
198
0
        match lit {
199
0
            Literal::Integer(i) => Ok(i.to_string()),
200
0
            Literal::Float(f) => Ok(f.to_string()),
201
0
            Literal::String(s) => Ok(format!("\"{}\"", s.replace('"', "\\\""))),
202
0
            Literal::Bool(b) => Ok(b.to_string()),
203
0
            Literal::Char(c) => Ok(format!("'{c}'")),
204
0
            Literal::Unit => Ok("()".to_string()),
205
        }
206
0
    }
207
    
208
0
    fn gen_binary_op(op: BinaryOp) -> &'static str {
209
0
        match op {
210
0
            BinaryOp::Add => "+",
211
0
            BinaryOp::Subtract => "-", 
212
0
            BinaryOp::Multiply => "*",
213
0
            BinaryOp::Divide => "/",
214
0
            BinaryOp::Modulo => "%",
215
0
            BinaryOp::Equal => "==",
216
0
            BinaryOp::NotEqual => "!=",
217
0
            BinaryOp::Less => "<",
218
0
            BinaryOp::LessEqual => "<=",
219
0
            BinaryOp::Greater => ">",
220
0
            BinaryOp::GreaterEqual => ">=",
221
0
            BinaryOp::And => "&&",
222
0
            BinaryOp::Or => "||",
223
0
            BinaryOp::NullCoalesce => "??", // JavaScript-style null coalescing
224
0
            BinaryOp::BitwiseAnd => "&",
225
0
            BinaryOp::BitwiseOr => "|",
226
0
            BinaryOp::BitwiseXor => "^",
227
0
            BinaryOp::LeftShift => "<<",
228
0
            BinaryOp::Power => "pow", // Will need function call wrapper
229
        }
230
0
    }
231
    
232
0
    fn gen_unary_op(op: UnaryOp) -> &'static str {
233
0
        match op {
234
0
            UnaryOp::Not => "!",
235
0
            UnaryOp::Negate => "-",
236
0
            UnaryOp::BitwiseNot => "~",
237
0
            UnaryOp::Reference => "&",
238
        }
239
0
    }
240
    
241
0
    fn gen_pattern(pattern: &Pattern) -> Result<String> {
242
0
        match pattern {
243
0
            Pattern::Wildcard => Ok("_".to_string()),
244
0
            Pattern::Literal(lit) => Self::gen_literal(lit),
245
0
            Pattern::Identifier(name) => Ok(name.clone()),
246
0
            Pattern::List(patterns) => {
247
0
                let pattern_codes = patterns.iter()
248
0
                    .map(Self::gen_pattern)
249
0
                    .collect::<Result<Vec<_>>>()?;
250
0
                let patterns = pattern_codes.join(", ");
251
0
                Ok(format!("[{patterns}]"))
252
            }
253
0
            Pattern::Ok(inner) => {
254
0
                let inner_code = Self::gen_pattern(inner)?;
255
0
                Ok(format!("Ok({inner_code})"))
256
            }
257
0
            Pattern::Err(inner) => {
258
0
                let inner_code = Self::gen_pattern(inner)?;
259
0
                Ok(format!("Err({inner_code})"))
260
            }
261
0
            Pattern::Some(inner) => {
262
0
                let inner_code = Self::gen_pattern(inner)?;
263
0
                Ok(format!("Some({inner_code})"))
264
            }
265
0
            Pattern::None => Ok("None".to_string()),
266
0
            _ => Ok("_".to_string()), // Simplified for MVP
267
        }
268
0
    }
269
    
270
    // Type generation simplified for MVP - focus on minimal working compiler
271
    #[allow(dead_code)]
272
0
    fn gen_type(_ty: &crate::frontend::ast::Type) -> Result<String> {
273
0
        Ok("String".to_string()) // Simplified for self-hosting MVP
274
0
    }
275
    
276
    /// Generate complete Rust program for self-hosting
277
0
    pub fn gen_program(expr: &Expr) -> Result<String> {
278
0
        let main_code = Self::gen_expr(expr)?;
279
0
        Ok(format!(
280
0
            "use std::collections::HashMap;\n\n{main_code}"
281
0
        ))
282
0
    }
283
}
284
285
#[cfg(test)]
286
mod tests {
287
    use super::*;
288
    use crate::frontend::parser::Parser;
289
    
290
    fn gen_str(input: &str) -> Result<String> {
291
        let mut parser = Parser::new(input);
292
        let expr = parser.parse()?;
293
        MinimalCodeGen::gen_expr(&expr)
294
    }
295
    
296
    #[test]
297
    fn test_basic_expressions() {
298
        assert_eq!(gen_str("42").unwrap(), "42");
299
        assert_eq!(gen_str("true").unwrap(), "true");
300
        assert_eq!(gen_str("\"hello\"").unwrap(), "\"hello\"");
301
    }
302
    
303
    #[test] 
304
    fn test_binary_ops() {
305
        assert_eq!(gen_str("1 + 2").unwrap(), "(1 + 2)");
306
        assert_eq!(gen_str("x * y").unwrap(), "(x * y)");
307
    }
308
    
309
    #[test]
310
    fn test_function_def() {
311
        let result = gen_str("fun add(x: i32, y: i32) -> i32 { x + y }").unwrap();
312
        assert!(result.contains("fn add(x: i32, y: i32)"));
313
    }
314
    
315
    #[test]
316
    fn test_lambda() {
317
        assert_eq!(gen_str("|x| x + 1").unwrap(), "|x| (x + 1)");
318
    }
319
    
320
    #[test]
321
    fn test_list() {
322
        assert_eq!(gen_str("[1, 2, 3]").unwrap(), "vec![1, 2, 3]");
323
    }
324
}