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, std_source};
22pub use codespan::Span;
23pub use discover::{DiscoverParams, discover};
24pub use format::format;
25pub use parser::parse_path;
26pub use project::{Project, SourceTree, SymbolInfo};
27
28pub use lutra_bin::{ir, rr};
29
30#[derive(Debug, Clone, Copy)]
32#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
33pub enum ProgramRepr {
34 SqlPg,
35 SqlDuckdb,
36 BytecodeLt,
37}
38
39#[derive(Clone)]
41#[cfg_attr(feature = "clap", derive(clap::Parser))]
42pub struct CompileParams {
43 #[cfg_attr(feature = "clap", clap(long, default_value = "main"))]
48 program: String,
49
50 #[cfg_attr(feature = "clap", clap(skip))]
53 program_name_hint: Option<String>,
54
55 #[cfg_attr(feature = "clap", clap(long, default_value = "bytecode-lt"))]
57 repr: ProgramRepr,
58
59 #[cfg_attr(feature = "clap", clap(skip))]
64 externals: Vec<std::borrow::Cow<'static, str>>,
65}
66
67pub fn compile(
68 project: &Project,
69 params: &CompileParams,
70) -> Result<(rr::Program, rr::ProgramType), error::Error> {
71 let program_pr = self::check::check_overlay(
73 project,
74 ¶ms.program,
75 params.program_name_hint.as_deref(),
76 )?;
77
78 let program_ir = intermediate::lowerer::lower_expr(project, &program_pr);
80 tracing::debug!("ir:\n{}\n", lutra_bin::ir::print_no_color(&program_ir));
81
82 let program_ir = intermediate::validate_externals(program_ir, ¶ms.externals)?;
83
84 let program_ir = intermediate::inline(program_ir);
86 tracing::debug!(
87 "ir (inlined):\n{}\n",
88 lutra_bin::ir::print_no_color(&program_ir)
89 );
90 let program_ir = intermediate::layouter::on_program(program_ir);
91
92 let program = match params.repr {
94 ProgramRepr::SqlPg => rr::Program::SqlPostgres(Box::new(sql::compile_ir(
95 &program_ir,
96 sql::Dialect::Postgres,
97 ))),
98 ProgramRepr::SqlDuckdb => {
99 rr::Program::SqlDuckDb(Box::new(sql::compile_ir(&program_ir, sql::Dialect::DuckDB)))
100 }
101 ProgramRepr::BytecodeLt => {
102 rr::Program::BytecodeLt(bytecoding::compile_program(program_ir.clone()))
103 }
104 };
105
106 let ir_func_ty = program_ir.main.ty.kind.into_function().unwrap();
108 let ty = rr::ProgramType {
109 input: ir_func_ty.params.into_iter().next().unwrap(),
110 output: ir_func_ty.body,
111 defs: program_ir.defs,
112 };
113
114 Ok((program, ty))
115}
116
117pub fn project_to_types(project: &Project) -> ir::Module {
118 let module = intermediate::lowerer::lower_type_defs(project);
119
120 intermediate::layouter::on_root_module(module)
121}
122
123#[doc(hidden)]
126pub mod _lexer {
127 pub use crate::diagnostic::Diagnostic;
128 pub use crate::parser::{Token, TokenKind, lex_source_recovery as lex};
129}
130
131#[doc(hidden)]
134pub mod _bench {
135 pub use crate::diagnostic::Diagnostic;
136 pub use crate::parser::{parse_expr, parse_source};
137}
138
139#[track_caller]
140pub fn _test_compile_ty(ty_source: &str) -> (ir::Ty, Vec<ir::TyDef>) {
141 let source = SourceTree::empty();
142 let project = check(source, Default::default()).unwrap_or_else(|e| panic!("{e}"));
143
144 let program = format!("func (x: {ty_source}) -> x");
145 let program = check_overlay(&project, &program, None).unwrap();
146 let program = intermediate::lowerer::lower_expr(&project, &program);
147 let program = intermediate::layouter::on_program(program);
148 (program.get_input_ty().clone(), program.defs)
149}
150
151#[doc(hidden)]
152pub fn _test_compile_main(source: &str) -> Result<ir::Program, error::Error> {
153 let source = SourceTree::single("".into(), source.to_string());
154 let project = check(source, Default::default())?;
155 _test_compile_main_in(&project)
156}
157
158#[doc(hidden)]
159pub fn _test_compile_main_in(project: &Project) -> Result<ir::Program, error::Error> {
160 let main = check_overlay(project, "main", None)?;
161 let program = intermediate::lowerer::lower_expr(project, &main);
162 Ok(intermediate::layouter::on_program(program))
163}
164
165#[doc(hidden)]
166pub fn _test_inline(program: ir::Program) -> ir::Program {
167 intermediate::inline(program)
168}
169
170#[doc(hidden)]
171pub fn _test_layout(program: ir::Program) -> ir::Program {
172 intermediate::layouter::on_program(program)
173}
174
175impl std::fmt::Display for ProgramRepr {
176 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
177 match self {
178 Self::SqlPg => write!(f, "sql-pg"),
179 Self::SqlDuckdb => write!(f, "sql-duckdb"),
180 Self::BytecodeLt => write!(f, "bytecode-lt"),
181 }
182 }
183}
184
185impl ProgramRepr {
186 fn get_implicit_externals(&self) -> Vec<std::borrow::Cow<'static, str>> {
187 match self {
188 ProgramRepr::SqlPg => vec!["std::sql".into()],
189 ProgramRepr::SqlDuckdb => vec!["std::sql".into(), "std::fs".into()],
190 ProgramRepr::BytecodeLt => vec![],
191 }
192 }
193
194 pub fn tag(&self) -> &'static str {
196 match self {
197 ProgramRepr::SqlPg => "repr:sql-pg",
198 ProgramRepr::SqlDuckdb => "repr:sql-duckdb",
199 ProgramRepr::BytecodeLt => "repr:bytecode-lt",
200 }
201 }
202
203 pub fn from_externals<S: AsRef<str>>(externals: &[S]) -> Option<Self> {
207 for ext in externals {
208 match ext.as_ref() {
209 "repr:sql-pg" | "repr:sql-postgres" => return Some(ProgramRepr::SqlPg),
210 "repr:sql-duckdb" => return Some(ProgramRepr::SqlDuckdb),
211 "repr:bytecode-lt" => return Some(ProgramRepr::BytecodeLt),
212 _ => {}
213 }
214 }
215 None
216 }
217}
218
219impl CompileParams {
220 pub fn new(program: impl Into<String>, repr: ProgramRepr) -> Self {
221 Self {
222 program: program.into(),
223 program_name_hint: None,
224 repr,
225 externals: repr.get_implicit_externals(),
226 }
227 }
228
229 pub fn from_externals<S: AsRef<str>>(
234 program: impl Into<String>,
235 externals: &[S],
236 ) -> Result<Self, String> {
237 let repr = ProgramRepr::from_externals(externals).ok_or_else(|| {
238 "runner did not provide a program repr tag in get_externals()".to_string()
239 })?;
240 let externals: Vec<std::borrow::Cow<'static, str>> = externals
241 .iter()
242 .map(|s| std::borrow::Cow::Owned(s.as_ref().to_string()))
243 .collect();
244 Ok(Self {
245 program: program.into(),
246 program_name_hint: None,
247 repr,
248 externals,
249 })
250 }
251
252 pub fn with_program_name_hint(mut self, hint: impl Into<String>) -> Self {
253 self.program_name_hint = Some(hint.into());
254 self
255 }
256
257 pub fn with_externals<I, T>(mut self, externals: I) -> Self
258 where
259 I: IntoIterator<Item = T>,
260 T: Into<std::borrow::Cow<'static, str>>,
261 {
262 self.externals
263 .extend(externals.into_iter().map(|x| x.into()));
264 self
265 }
266
267 pub fn with_external(mut self, external: impl Into<std::borrow::Cow<'static, str>>) -> Self {
268 self.externals.push(external.into());
269 self
270 }
271
272 pub fn repr(&self) -> ProgramRepr {
273 self.repr
274 }
275}