Skip to main content

stoffel/
compiler.rs

1//! Stoffel-Lang compilation and bytecode loading helpers.
2//!
3//! This module is a thin SDK boundary over `stoffellang`; it translates compiler
4//! and bytecode errors into SDK errors without reimplementing the language
5//! compiler.
6
7use std::io::Cursor;
8use std::path::Path;
9
10use stoffel_vm_types::compiled_binary::CompiledBinary;
11
12use crate::backend::MpcBackend;
13use crate::error::{format_compiler_errors, Error, Result};
14use crate::program::Program;
15
16#[derive(Debug, Clone, Default, PartialEq, Eq)]
17pub struct CompilationOptions {
18    pub optimize: bool,
19    pub optimization_level: u8,
20    pub print_ir: bool,
21    pub entry_points: Vec<String>,
22}
23
24pub fn compile_source(source: &str, filename: &str, backend: MpcBackend) -> Result<Program> {
25    compile_source_with_options(source, filename, backend, CompilationOptions::default())
26}
27
28pub fn compile_source_with_options(
29    source: &str,
30    filename: &str,
31    backend: MpcBackend,
32    options: CompilationOptions,
33) -> Result<Program> {
34    let options = compiler_options(backend, options);
35    let compiled = stoffellang::compile(source, filename, &options)
36        .map_err(|errors| Error::Compilation(format_compiler_errors(&errors)))?;
37    Ok(Program::new(stoffellang::convert_to_binary(&compiled)))
38}
39
40pub fn compile_file(path: &Path, backend: MpcBackend) -> Result<Program> {
41    compile_file_with_options(path, backend, CompilationOptions::default())
42}
43
44pub fn compile_file_with_options(
45    path: &Path,
46    backend: MpcBackend,
47    options: CompilationOptions,
48) -> Result<Program> {
49    let source = std::fs::read_to_string(path)?;
50    let options = compiler_options(backend, options);
51    let compiled = stoffellang::compile_file(path, &source, &options)
52        .map_err(|errors| Error::Compilation(format_compiler_errors(&errors)))?;
53    Ok(Program::new(stoffellang::convert_to_binary(&compiled)))
54}
55
56pub fn load_bytecode(bytecode: &[u8]) -> Result<Program> {
57    let mut cursor = Cursor::new(bytecode);
58    let binary = CompiledBinary::deserialize(&mut cursor)
59        .map_err(|error| Error::Bytecode(format!("{error:?}")))?;
60    if cursor.position() != bytecode.len() as u64 {
61        return Err(Error::Bytecode(format!(
62            "bytecode contains {} trailing byte(s)",
63            bytecode.len() as u64 - cursor.position()
64        )));
65    }
66    Ok(Program::new(binary))
67}
68
69fn compiler_options(
70    backend: MpcBackend,
71    options: CompilationOptions,
72) -> stoffellang::CompilerOptions {
73    // Optimizer budget knobs are honored here, at the SDK boundary, rather than
74    // inside the optimizer — keeping the library compile path hermetic. The
75    // Stoffel CLI populates these env vars from the project's build config.
76    let env_budget = |name: &str| std::env::var(name).ok().and_then(|v| v.parse().ok());
77    stoffellang::CompilerOptions {
78        optimize: options.optimize || options.optimization_level > 0,
79        optimization_level: options.optimization_level,
80        print_ir: options.print_ir,
81        mpc_backend: backend.compiler_backend(),
82        mpc_curve: backend.compiler_curve(),
83        entry_points: options.entry_points,
84        inline_budget: env_budget("STOFFEL_INLINE_BUDGET"),
85        unroll_budget: env_budget("STOFFEL_UNROLL_BUDGET"),
86        unroll_max_expansion: env_budget("STOFFEL_UNROLL_MAX_EXPANSION"),
87    }
88}