seqc/
lib.rs

1//! Seq Compiler Library
2//!
3//! Provides compilation from .seq source to LLVM IR and executable binaries.
4
5pub mod ast;
6pub mod builtins;
7pub mod codegen;
8pub mod parser;
9pub mod resolver;
10pub mod typechecker;
11pub mod types;
12pub mod unification;
13
14pub use ast::Program;
15pub use codegen::CodeGen;
16pub use parser::Parser;
17pub use resolver::{Resolver, check_collisions, find_stdlib};
18pub use typechecker::TypeChecker;
19pub use types::{Effect, StackType, Type};
20
21use std::fs;
22use std::path::Path;
23use std::process::Command;
24
25/// Compile a .seq source file to an executable
26pub fn compile_file(source_path: &Path, output_path: &Path, keep_ir: bool) -> Result<(), String> {
27    // Read source file
28    let source = fs::read_to_string(source_path)
29        .map_err(|e| format!("Failed to read source file: {}", e))?;
30
31    // Parse
32    let mut parser = Parser::new(&source);
33    let program = parser.parse()?;
34
35    // Resolve includes (if any)
36    let program = if !program.includes.is_empty() {
37        let stdlib_path = find_stdlib()?;
38        let mut resolver = Resolver::new(stdlib_path);
39        resolver.resolve(source_path, program)?
40    } else {
41        program
42    };
43
44    // Check for word name collisions
45    check_collisions(&program.words)?;
46
47    // Verify we have a main word
48    if program.find_word("main").is_none() {
49        return Err("No main word defined".to_string());
50    }
51
52    // Validate all word calls reference defined words or built-ins
53    program.validate_word_calls()?;
54
55    // Type check (validates stack effects, especially for conditionals)
56    let mut type_checker = TypeChecker::new();
57    type_checker.check_program(&program)?;
58
59    // Extract inferred quotation types (in DFS traversal order)
60    let quotation_types = type_checker.take_quotation_types();
61
62    // Generate LLVM IR with type information
63    let mut codegen = CodeGen::new();
64    let ir = codegen.codegen_program(&program, quotation_types)?;
65
66    // Write IR to file
67    let ir_path = output_path.with_extension("ll");
68    fs::write(&ir_path, ir).map_err(|e| format!("Failed to write IR file: {}", e))?;
69
70    // Validate runtime library exists
71    let runtime_lib = Path::new("target/release/libseq_runtime.a");
72    if !runtime_lib.exists() {
73        return Err(format!(
74            "Runtime library not found at {}. \
75             Please run 'cargo build --release -p seq-runtime' first.",
76            runtime_lib.display()
77        ));
78    }
79
80    // Compile IR to executable using clang
81    let output = Command::new("clang")
82        .arg(&ir_path)
83        .arg("-o")
84        .arg(output_path)
85        .arg("-L")
86        .arg("target/release")
87        .arg("-lseq_runtime")
88        .output()
89        .map_err(|e| format!("Failed to run clang: {}", e))?;
90
91    if !output.status.success() {
92        let stderr = String::from_utf8_lossy(&output.stderr);
93        return Err(format!("Clang compilation failed:\n{}", stderr));
94    }
95
96    // Remove temporary IR file unless user wants to keep it
97    if !keep_ir {
98        fs::remove_file(&ir_path).ok();
99    }
100
101    Ok(())
102}
103
104/// Compile source string to LLVM IR string (for testing)
105pub fn compile_to_ir(source: &str) -> Result<String, String> {
106    let mut parser = Parser::new(source);
107    let program = parser.parse()?;
108
109    program.validate_word_calls()?;
110
111    let mut type_checker = TypeChecker::new();
112    type_checker.check_program(&program)?;
113
114    let quotation_types = type_checker.take_quotation_types();
115
116    let mut codegen = CodeGen::new();
117    codegen.codegen_program(&program, quotation_types)
118}