Skip to main content

renderbox_sdk/compile/
pipeline.rs

1use std::path::Path;
2
3use renderbox_dsl::{Arena, NodeId, OpNode};
4use renderbox_dsl::backend::codegen::codegen;
5use renderbox_dsl::diag::error::{Diagnostics, render_diagnostic, render_diagnostics_json};
6use renderbox_dsl::sema::module::{FileModuleResolver, load_modules};
7use renderbox_dsl::sema::op_registry::OpRegistry;
8use renderbox_dsl::sema::resolve::resolve;
9use renderbox_dsl::sema::typeck::typecheck;
10use renderbox_dsl::syntax::lexer::Lexer;
11use renderbox_dsl::syntax::parser::Parser;
12
13// ---------------------------------------------------------------------------
14// CompileError
15// ---------------------------------------------------------------------------
16
17pub enum CompileError {
18    Dsl(Diagnostics),
19    Plan(String),
20}
21
22impl std::fmt::Debug for CompileError {
23    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24        match self {
25            CompileError::Dsl(diags) => write!(f, "CompileError::Dsl({diags:?})"),
26            CompileError::Plan(err) => write!(f, "CompileError::Plan({err})"),
27        }
28    }
29}
30
31impl CompileError {
32    pub fn render(&self, source: &str, filename: &str) -> String {
33        match self {
34            CompileError::Dsl(diags) => {
35                let mut out = String::new();
36                for diag in diags.iter() {
37                    if !out.is_empty() {
38                        out.push('\n');
39                    }
40                    out.push_str(&render_diagnostic(source, filename, diag));
41                }
42                out
43            }
44            CompileError::Plan(err) => format!("plan error: {err}"),
45        }
46    }
47
48    pub fn render_json(&self, source: &str, filename: &str) -> String {
49        match self {
50            CompileError::Dsl(diags) => render_diagnostics_json(source, filename, diags),
51            CompileError::Plan(err) => {
52                format!(r#"{{"type":"plan","message":"{}"}}"#, err)
53            }
54        }
55    }
56}
57
58// ---------------------------------------------------------------------------
59// Front-end pipeline
60// ---------------------------------------------------------------------------
61
62pub(crate) struct FrontendResult {
63    pub arena: Arena<OpNode>,
64    pub outputs: Vec<NodeId>,
65}
66
67pub(crate) fn run_frontend(source: &str, base_dir: &Path) -> Result<FrontendResult, CompileError> {
68    let registry = OpRegistry::builtin();
69
70    let tokens = Lexer::tokenize(source);
71    let parser = Parser::new(source, tokens, &registry);
72    let (program, diags) = parser.parse_program();
73    check(diags)?;
74
75    let (resolve_result, diags) = resolve(&program, &registry);
76    check(diags)?;
77
78    let resolver = FileModuleResolver::new(base_dir.to_path_buf());
79    let (module_env, diags) = load_modules(&program, &registry, &resolver);
80    check(diags)?;
81
82    let (typeck_result, diags) = typecheck(&program, &resolve_result, &registry, &module_env);
83    check(diags)?;
84
85    let (codegen_result, diags) = codegen(&program, &resolve_result, &typeck_result, &registry, &module_env);
86    check(diags)?;
87
88    Ok(FrontendResult {
89        arena: codegen_result.arena,
90        outputs: codegen_result.outputs,
91    })
92}
93
94pub(crate) fn check(diags: Diagnostics) -> Result<(), CompileError> {
95    if diags.has_errors() {
96        return Err(CompileError::Dsl(diags));
97    }
98    Ok(())
99}