mod lexer;
mod parser;
mod ast;
mod sema;
mod codegen;
use clap::Parser as ClapParser;
use inkwell::context::Context;
use std::fs;
#[derive(ClapParser, Debug)]
#[command(author, version, about = "Sharp Programming Language Compiler", long_about = None)]
struct Args {
#[arg(required = true)]
input: String,
#[arg(long)]
emit_llvm: bool,
#[arg(long)]
emit_obj: bool,
#[arg(long)]
emit_binary: bool,
#[arg(short = 'o', long)]
output: Option<String>,
#[arg(long)]
check: bool,
#[arg(long)]
dump_tokens: bool,
}
fn main() {
let args = Args::parse();
println!("Compiling: {}", args.input);
let source = match fs::read_to_string(&args.input) {
Ok(s) => s,
Err(e) => {
eprintln!("Error reading file '{}': {}", args.input, e);
std::process::exit(1);
}
};
println!("Phase 1: Lexing...");
let lexer = lexer::Lexer::new(&source);
let tokens: Vec<_> = lexer.collect();
println!(" {} tokens generated", tokens.len());
if args.dump_tokens {
for (tok, span) in &tokens {
println!(" {:?} @ {}..{}", tok, span.start, span.end);
}
}
println!("Phase 2: Parsing...");
let mut parser = parser::Parser::new(&source);
let program = match parser.parse_program() {
Ok(p) => {
println!(" {} top-level items parsed", p.items.len());
p
}
Err(e) => {
eprintln!("Parse error: {}", e);
std::process::exit(1);
}
};
println!("Phase 3: Semantic analysis...");
let mut analyzer = sema::Analyzer::new();
if let Err(e) = analyzer.analyze(&program) {
eprintln!("Semantic error: {}", e);
std::process::exit(1);
}
println!(" Type checking completed");
if args.check {
println!("Check completed successfully!");
return;
}
println!("Phase 4: Code generation...");
let context = Context::create();
let mut codegen = codegen::Codegen::new(&context, "sharp_module");
if let Err(e) = codegen.compile_program(&program) {
eprintln!("Codegen error: {}", e);
std::process::exit(1);
}
println!(" LLVM IR generated");
if args.emit_llvm {
let output_file = args.output.unwrap_or_else(|| {
args.input.replace(".shrp", ".ll")
});
if let Err(e) = codegen.write_ir(&output_file) {
eprintln!("Error writing LLVM IR: {}", e);
std::process::exit(1);
}
println!("LLVM IR written to: {}", output_file);
} else if args.emit_obj {
let output_file = args.output.unwrap_or_else(|| {
args.input.replace(".shrp", ".o")
});
if let Err(e) = codegen.write_object(&output_file) {
eprintln!("Error writing object file: {}", e);
std::process::exit(1);
}
println!("Object file written to: {}", output_file);
} else if args.emit_binary {
let obj_file = args.output.as_ref()
.map(|o| o.replace(".out", ".o"))
.unwrap_or_else(|| args.input.replace(".shrp", ".o"));
if let Err(e) = codegen.write_object(&obj_file) {
eprintln!("Error writing object file: {}", e);
std::process::exit(1);
}
let bin_file = args.output.unwrap_or_else(|| args.input.replace(".shrp", ""));
println!("Linking to binary: {}", bin_file);
let status = std::process::Command::new("clang")
.arg(&obj_file)
.arg("-o")
.arg(&bin_file)
.status();
match status {
Ok(s) if s.success() => println!("Binary written to: {}", bin_file),
Ok(s) => {
eprintln!("Linker failed with status: {}", s);
std::process::exit(1);
}
Err(e) => {
eprintln!("Failed to invoke linker: {}", e);
std::process::exit(1);
}
}
} else {
codegen.print_ir();
}
println!("\nCompilation completed successfully!");
}