sharp 0.1.0

A modern, statically-typed programming language with Python-like syntax, compiled to native code via LLVM. Game engine ready!
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 {
    /// Input Sharp source file (.shrp)
    #[arg(required = true)]
    input: String,

    /// Emit LLVM IR to file
    #[arg(long)]
    emit_llvm: bool,

    /// Emit object file (.o)
    #[arg(long)]
    emit_obj: bool,

    /// Emit native binary (links object)
    #[arg(long)]
    emit_binary: bool,

    /// Output file path
    #[arg(short = 'o', long)]
    output: Option<String>,

    /// Only check for errors, don't generate code
    #[arg(long)]
    check: bool,

    /// Dump all tokens for debugging
    #[arg(long)]
    dump_tokens: bool,
}

fn main() {
    let args = Args::parse();
    
    println!("Compiling: {}", args.input);
    
    // Read source file
    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);
        }
    };
    
    // Phase 1: Lexical analysis
    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);
        }
    }
    
    // Phase 2: Parsing
    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);
        }
    };
    
    // Phase 3: Semantic analysis
    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;
    }
    
    // Phase 4: Code generation
    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");
    
    // Emit LLVM IR if requested
    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 {
        // Write temporary object then link with clang
        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!");
}