Skip to main content

brink_compiler/
lib.rs

1//! Compiler for inkle's ink narrative scripting language.
2//!
3//! Orchestrates the full compilation pipeline: file discovery, parsing
4//! (`brink-syntax`), HIR lowering (`brink-ir`), semantic analysis
5//! (`brink-analyzer`), and codegen into the `brink-format` binary
6//! representation consumed by `brink-runtime`.
7
8mod driver;
9
10pub use brink_driver::AnalysisOptions;
11pub use brink_ir::FileId;
12
13use brink_format::StoryData;
14use brink_ir::Diagnostic;
15use std::io;
16use std::path::Path;
17
18/// Successful compilation output, including any non-fatal warnings.
19#[derive(Debug)]
20pub struct CompileOutput {
21    pub data: StoryData,
22    pub warnings: Vec<Diagnostic>,
23}
24
25/// Successful LIR compilation output, including any non-fatal warnings.
26pub struct LirOutput {
27    pub program: brink_ir::lir::Program,
28    pub warnings: Vec<Diagnostic>,
29}
30
31/// Compile an ink story from an entry-point file path.
32///
33/// Reads files from disk, follows INCLUDEs, and runs the full compilation
34/// pipeline. Returns the compiled story data or a list of diagnostics.
35pub fn compile_path(path: &Path) -> Result<CompileOutput, CompileError> {
36    compile(path.to_string_lossy().as_ref(), |p| {
37        std::fs::read_to_string(p).map_err(|e| io::Error::new(e.kind(), format!("{p}: {e}")))
38    })
39}
40
41/// Compile an ink story with caller-provided file reading.
42///
43/// The `read_file` callback is called for the entry point and each
44/// `INCLUDE`d file discovered during parsing. This enables compilation in
45/// WASM, tests, and editor contexts where files are not on disk.
46pub fn compile<F>(entry: &str, read_file: F) -> Result<CompileOutput, CompileError>
47where
48    F: FnMut(&str) -> Result<String, io::Error>,
49{
50    driver::compile(entry, read_file)
51}
52
53/// Compile with explicit analysis options — e.g. a registered host-capability
54/// manifest and external-check severity (the "compiler flag, error by
55/// default"). Manifest-driven diagnostics are surfaced as compile warnings or
56/// errors per the severity policy.
57pub fn compile_with_options<F>(
58    entry: &str,
59    read_file: F,
60    options: AnalysisOptions,
61) -> Result<CompileOutput, CompileError>
62where
63    F: FnMut(&str) -> Result<String, io::Error>,
64{
65    driver::compile_with_options(entry, read_file, options)
66}
67
68/// Compile an ink story to the ink.json format (same as inklecate output).
69///
70/// Useful for diffing brink's output against the reference compiler.
71pub fn compile_to_json<F>(entry: &str, read_file: F) -> Result<brink_json::InkJson, CompileError>
72where
73    F: FnMut(&str) -> Result<String, io::Error>,
74{
75    let lir_output = driver::compile_to_lir(entry, read_file)?;
76    Ok(brink_codegen_json::emit(&lir_output.program))
77}
78
79/// Compile ink source from a string to the ink.json format.
80pub fn compile_string_to_json(source: &str) -> Result<brink_json::InkJson, CompileError> {
81    compile_to_json("<string>", |_| Ok(source.to_string()))
82}
83
84/// Errors that can occur during compilation.
85#[derive(Debug, thiserror::Error)]
86pub enum CompileError {
87    /// File I/O error (missing file, permission denied, etc.).
88    #[error("I/O error: {0}")]
89    Io(#[from] io::Error),
90    /// One or more diagnostics prevented compilation.
91    #[error("{} diagnostic(s) prevented compilation", .0.len())]
92    Diagnostics(Vec<Diagnostic>),
93    /// Circular INCLUDE dependency detected.
94    #[error("circular INCLUDE dependency: {0}")]
95    CircularInclude(String),
96}
97
98impl From<brink_driver::DiscoverError> for CompileError {
99    fn from(err: brink_driver::DiscoverError) -> Self {
100        match err {
101            brink_driver::DiscoverError::Io(e) => Self::Io(e),
102            brink_driver::DiscoverError::CircularInclude(msg) => Self::CircularInclude(msg),
103        }
104    }
105}