๐Ÿ“ฆ huangsam / virtuc

๐Ÿ“„ lib.rs ยท 84 lines
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84//! # VirtuC Compiler Library
//!
//! This is the main library crate for the `virtuc` compiler, a Rust-based compiler
//! for a minimal subset of the C programming language. The compiler supports only
//! primitives (int, float), for loops, if-elseif-else statements, and functions.
//!
//! ## Architecture
//!
//! The compiler follows a standard compilation pipeline:
//! 1. **Lexing**: Source code โ†’ Tokens
//! 2. **Parsing**: Tokens โ†’ Abstract Syntax Tree (AST)
//! 3. **Semantic Analysis**: AST validation and type checking
//! 4. **Code Generation**: AST โ†’ LLVM Intermediate Representation (IR)
//! 5. **Execution**: IR โ†’ Native executable

pub mod ast;
pub mod codegen;
pub mod error;
pub mod header_registry;
pub mod lexer;
pub mod parser;
pub mod semantic;

use std::fs;
use std::path::Path;
use std::process::Command;

/// Compiles a C subset source string to an executable at the specified output path.
///
/// # Arguments
///
/// * `source` - The source code string.
/// * `output` - The path where the executable should be written.
///
/// # Returns
///
/// * `Result<(), Box<dyn std::error::Error>>` - Ok if compilation succeeds, Err otherwise.
pub fn compile(source: &str, output: &Path) -> Result<(), Box<dyn std::error::Error>> {
    // Lexical analysis
    let tokens = lexer::lex(source)?;

    // Parsing
    let ast = parser::parse(&tokens)?;

    // Semantic analysis
    let errors = semantic::analyze(&ast);
    if !errors.is_empty() {
        let error_msg = errors
            .iter()
            .map(|e| e.to_string())
            .collect::<Vec<_>>()
            .join("\n");
        return Err(format!("Semantic errors:\n{}", error_msg).into());
    }

    // Code generation
    let ir = codegen::generate_ir(&ast)?;

    // Write IR to temporary file
    // Use output path with .ll extension
    let ir_file = output.with_extension("ll");
    fs::write(&ir_file, &ir)?;

    // Compile IR to executable using clang
    let status = Command::new("clang")
        .args([
            ir_file.to_str().unwrap(),
            "-o",
            output.to_str().unwrap(),
            "-lc",
            "-Wno-override-module",
        ])
        .status()?;

    if !status.success() {
        return Err("Compilation failed".into());
    }

    // Clean up IR file
    let _ = fs::remove_file(ir_file);

    Ok(())
}