#![deny(unsafe_code)]
#![warn(missing_docs)]
pub mod ast;
pub mod backend;
pub mod builtin;
pub mod error;
pub mod ir;
pub mod lexer;
pub mod lower;
pub mod parser;
pub mod prelude;
pub mod program;
pub mod schedule;
pub mod serde_def;
pub mod types;
pub use error::{CompileError, Span};
pub use program::RillProgram;
pub use serde_def::{compile_def, RillLangDef};
pub use builtin::{BuiltinKind, BuiltinSig, Registry, SampleBuiltin};
use rill_core::math::Transcendental;
pub fn compile<T: Transcendental>(src: &str) -> Result<RillProgram<T>, CompileError> {
let tokens = lexer::tokenize(src)?;
let program = parser::parse(&tokens)?;
let typed = types::infer::infer_program(&program)?;
let ir = lower::lower(&typed)?;
Ok(RillProgram::<T>::new(ir))
}
pub fn compile_with<T: Transcendental>(
src: &str,
registry: &Registry<T>,
sample_rate: f32,
) -> Result<RillProgram<T>, CompileError> {
let tokens = lexer::tokenize(src)?;
let program = parser::parse(&tokens)?;
let typed = types::infer::infer_program_with(&program, registry)?;
let ir = lower::lower_with(&typed, registry, sample_rate)?;
validate_block_builtins(&ir)?;
RillProgram::<T>::new_with(ir, registry, sample_rate)
}
fn validate_block_builtins(ir: &crate::ir::Ir) -> Result<(), CompileError> {
use crate::ir::Instr;
use crate::schedule::{build_schedule, Step};
for instr in &ir.instrs {
if let Instr::CallSample { srcs, .. } = instr {
if srcs.len() > backend::interp::MAX_SAMPLE_BUILTIN_INS {
return Err(CompileError::Unsupported(format!(
"sample built-in has {} signal inputs; the maximum is {}",
srcs.len(),
backend::interp::MAX_SAMPLE_BUILTIN_INS,
)));
}
}
}
let sched = build_schedule(ir);
for step in &sched.steps {
if let Step::Sample(instrs) = step {
for &idx in instrs {
if matches!(ir.instrs[idx], Instr::CallBlock { .. }) {
return Err(CompileError::Unsupported(
"block built-in cannot be used inside a feedback loop (`~`)".to_string(),
));
}
}
}
}
Ok(())
}