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, ®istry);
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)
}