ts-native 0.1.4

A TypeScript to native executable compiler using Cranelift
use anyhow::Result;
use codegen::{CodeGen, HirExpr, BinOp};

mod codegen;
mod runtime;
mod ts_parser;
mod linker;
mod pe_builder;
mod extension;
mod config;

fn main() -> Result<()> {
    let args: Vec<String> = std::env::args().collect();
    
    if args.len() > 1 && args[1] == "--test" {
        return test_compile();
    }
    
    if args.len() < 2 {
        eprintln!("用法: ts-native <input.ts> [-o output]");
        eprintln!("      ts-native --test");
        eprintln!("      ts-native compile [--config ts-native.toml]");
        std::process::exit(1);
    }
    
    let mut registry = extension::ExtensionRegistry::new();
    
    let cargo_toml_path = std::path::PathBuf::from("Cargo.toml");
    if cargo_toml_path.exists() {
        println!("检测到 Cargo.toml,扫描扩展包...");
        registry = extension::discover_extensions_from_cargo(&cargo_toml_path)?;
        println!("加载了 {} 个扩展包", registry.extensions.len());
    }
    
    let input_file = &args[1];
    let source = std::fs::read_to_string(input_file)?;
    
    println!("\n解析 TypeScript: {}", input_file);
    let hir = ts_parser::parse(&source)?;
    
    println!("生成 native 代码...");
    let mut codegen = CodeGen::new();
    let binary = codegen.compile(&hir)?;
    
    let output_file = args.get(3).map(|s| s.as_str()).unwrap_or("a.o");
    std::fs::write(output_file, &binary)?;
    
    println!("✅ 输出: {} ({} bytes)", output_file, binary.len());
    
    if output_file.ends_with(".o") {
        let exe_file = output_file.replace(".o", ".exe");
        println!("\n尝试链接为可执行文件...");
        
        let link_result = try_link(output_file, &exe_file, &registry);
        match link_result {
            Ok(size) => println!("✅ 生成: {} ({} bytes)", exe_file, size),
            Err(e) => println!("⚠️  链接失败(需要安装链接器): {}", e),
        }
    }
    
    Ok(())
}

fn try_link(obj_file: &str, exe_file: &str, _registry: &extension::ExtensionRegistry) -> Result<usize> {
    let linker_instance = linker::Linker::new(exe_file);
    
    let has_start = std::path::Path::new("start_nocrt.o").exists();
    let has_runtime = std::path::Path::new("runtime_nocrt.o").exists();
    
    let mut obj_files: Vec<&str> = vec![obj_file];
    if has_start {
        obj_files.push("start_nocrt.o");
    }
    if has_runtime {
        obj_files.push("runtime_nocrt.o");
    }
    
    if let Ok(result) = linker_instance.link_with_clang(&obj_files) {
        return Ok(result.len());
    }
    
    if let Ok(result) = linker_instance.link_with_gcc(&obj_files) {
        return Ok(result.len());
    }
    
    if let Ok(result) = linker_instance.link(&obj_files) {
        return Ok(result.len());
    }
    
    anyhow::bail!("No suitable linker found (tried: clang, gcc, link)")
}

fn test_compile() -> Result<()> {
    println!("=== 测试 Cranelift 代码生成 ===\n");
    
    let hir = vec![
        HirExpr::Function {
            name: "add".to_string(),
            params: vec!["a".to_string(), "b".to_string()],
            body: vec![
                HirExpr::Return(Some(Box::new(
                    HirExpr::Binary {
                        op: BinOp::Add,
                        left: Box::new(HirExpr::Identifier("a".to_string())),
                        right: Box::new(HirExpr::Identifier("b".to_string())),
                    }
                ))),
            ],
        },
        HirExpr::Function {
            name: "main".to_string(),
            params: vec![],
            body: vec![
                HirExpr::Var {
                    name: "x".to_string(),
                    init: Some(Box::new(HirExpr::Number(10.0))),
                    is_mut: false,
                },
                HirExpr::Var {
                    name: "sum".to_string(),
                    init: Some(Box::new(HirExpr::Number(0.0))),
                    is_mut: true,
                },
                HirExpr::While {
                    cond: Box::new(HirExpr::Binary {
                        op: BinOp::Gt,
                        left: Box::new(HirExpr::Identifier("x".to_string())),
                        right: Box::new(HirExpr::Number(0.0)),
                    }),
                    body: vec![
                        HirExpr::Assign {
                            target: Box::new(HirExpr::Identifier("sum".to_string())),
                            value: Box::new(HirExpr::Binary {
                                op: BinOp::Add,
                                left: Box::new(HirExpr::Identifier("sum".to_string())),
                                right: Box::new(HirExpr::Identifier("x".to_string())),
                            }),
                        },
                        HirExpr::Assign {
                            target: Box::new(HirExpr::Identifier("x".to_string())),
                            value: Box::new(HirExpr::Binary {
                                op: BinOp::Sub,
                                left: Box::new(HirExpr::Identifier("x".to_string())),
                                right: Box::new(HirExpr::Number(1.0)),
                            }),
                        },
                    ],
                },
                HirExpr::Return(Some(Box::new(HirExpr::Identifier("sum".to_string())))),
            ],
        },
    ];
    
    println!("HIR:");
    for expr in &hir {
        println!("  {:?}", expr);
    }
    
    let mut codegen = CodeGen::new();
    let binary = codegen.compile(&hir)?;
    
    std::fs::write("test.o", &binary)?;
    println!("\n✅ 生成 test.o ({} bytes)", binary.len());
    println!("\n函数:");
    println!("  add(a, b) = a + b");
    println!("  main() = sum(1..10) = 55");
    
    Ok(())
}

fn parse_simple(source: &str) -> Result<Vec<HirExpr>> {
    let mut exprs = Vec::new();
    
    for line in source.lines() {
        let line = line.trim();
        if line.starts_with("function") {
            let line = line.strip_prefix("function").unwrap().trim();
            if let Some(paren_pos) = line.find('(') {
                let name = line[..paren_pos].trim().to_string();
                let rest = &line[paren_pos+1..];
                if let Some(end_paren) = rest.find(')') {
                    let params_str = &rest[..end_paren];
                    let params: Vec<String> = if params_str.is_empty() {
                        vec![]
                    } else {
                        params_str.split(',').map(|s| s.trim().to_string()).collect()
                    };
                    
                    exprs.push(HirExpr::Function {
                        name,
                        params,
                        body: vec![],
                    });
                }
            }
        }
    }
    
    if exprs.is_empty() {
        exprs.push(HirExpr::Function {
            name: "main".to_string(),
            params: vec![],
            body: vec![HirExpr::Return(Some(Box::new(HirExpr::Number(0.0))))],
        });
    }
    
    Ok(exprs)
}