Skip to main content

lutra_compiler/
lib.rs

1mod bytecoding;
2mod check;
3mod diagnostic;
4mod discover;
5mod format;
6mod intermediate;
7mod parser;
8mod project;
9mod resolver;
10mod sql;
11mod utils;
12
13type Result<T, E = diagnostic::Diagnostic> = core::result::Result<T, E>;
14
15pub mod codespan;
16pub mod error;
17pub mod pr;
18pub mod printer;
19
20pub use bytecoding::compile_program as bytecode_program;
21pub use check::{CheckParams, check, check_overlay};
22pub use codespan::Span;
23pub use discover::{DiscoverParams, discover};
24pub use format::format;
25pub use intermediate::inline;
26pub use parser::parse_path;
27pub use project::{Project, SourceTree, SymbolInfo};
28
29pub use lutra_bin::{ir, rr};
30
31#[derive(Debug, Clone, Copy)]
32#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
33pub enum ProgramFormat {
34    SqlPg,
35    SqlDuckdb,
36    BytecodeLt,
37}
38
39pub fn compile(
40    project: &Project,
41    program: &str,
42    name_hint: Option<&str>,
43    format: ProgramFormat,
44) -> Result<(rr::Program, rr::ProgramType), error::Error> {
45    // resolve
46    let program_pr = self::check::check_overlay(project, program, name_hint)?;
47
48    // lower
49    let program_ir = intermediate::lowerer::lower_expr(project, &program_pr);
50    tracing::debug!("ir:\n{}\n", lutra_bin::ir::print(&program_ir));
51
52    // intermediate optimizations
53    let program_ir = intermediate::inline(program_ir);
54    tracing::debug!("ir (inlined):\n{}\n", lutra_bin::ir::print(&program_ir));
55    let program_ir = intermediate::layouter::on_program(program_ir);
56
57    // backend (sql or bytecode)
58    let program = match format {
59        ProgramFormat::SqlPg => rr::Program::SqlPostgres(Box::new(sql::compile_ir(
60            &program_ir,
61            sql::Dialect::Postgres,
62        ))),
63        ProgramFormat::SqlDuckdb => {
64            rr::Program::SqlDuckDB(Box::new(sql::compile_ir(&program_ir, sql::Dialect::DuckDB)))
65        }
66        ProgramFormat::BytecodeLt => {
67            rr::Program::BytecodeLt(bytecoding::compile_program(program_ir.clone()))
68        }
69    };
70
71    // construct the program ty
72    let ir_func_ty = program_ir.main.ty.kind.into_function().unwrap();
73    let ty = rr::ProgramType {
74        input: ir_func_ty.params.into_iter().next().unwrap(),
75        output: ir_func_ty.body,
76        defs: program_ir.defs,
77    };
78
79    Ok((program, ty))
80}
81
82pub fn project_to_types(project: &Project) -> ir::Module {
83    let module = intermediate::lowerer::lower_type_defs(project);
84
85    intermediate::layouter::on_root_module(module)
86}
87
88/// Internal implementation detail, do not use.
89// Only exposed because I don't want to maintain a separate lexer for IR parser.
90#[doc(hidden)]
91pub mod _lexer {
92    pub use crate::diagnostic::Diagnostic;
93    pub use crate::parser::{Token, TokenKind, lex_source_recovery as lex};
94}
95
96/// Internal implementation detail, do not use.
97// Exposed for benchmarks.
98#[doc(hidden)]
99pub mod _bench {
100    pub use crate::check::{check_std_lib, parse_std_lib, std_lib};
101    pub use crate::diagnostic::Diagnostic;
102    pub use crate::parser::{parse_expr, parse_source};
103}
104
105#[track_caller]
106pub fn _test_compile_ty(ty_source: &str) -> ir::Ty {
107    let source = format!("type t: {ty_source}");
108
109    let source = SourceTree::single("".into(), source);
110    let project = check(source, Default::default()).unwrap_or_else(|e| panic!("{e}"));
111
112    let module = project_to_types(&project);
113
114    let item = module.decls.into_iter().next().unwrap();
115    assert_eq!(item.name, "t");
116    let ir::Decl::Type(mut ty) = item.decl else {
117        panic!()
118    };
119
120    ty.name = None;
121    ty
122}
123
124pub fn _test_compile_main(source: &str) -> Result<ir::Program, error::Error> {
125    let source = SourceTree::single("".into(), source.to_string());
126    let project = check(source, Default::default())?;
127    _test_compile_main_in(&project)
128}
129
130pub fn _test_compile_main_in(project: &Project) -> Result<ir::Program, error::Error> {
131    let main = check_overlay(project, "main", None)?;
132    let program = intermediate::lowerer::lower_expr(project, &main);
133    Ok(intermediate::layouter::on_program(program))
134}