Skip to main content

rill_lang/
lib.rs

1//! # rill-lang
2//!
3//! A Faust-style functional streaming DSL that compiles to a
4//! [`rill_core::Algorithm`]. See the crate guide for language details.
5
6#![deny(unsafe_code)]
7#![warn(missing_docs)]
8
9pub mod ast;
10pub mod backend;
11pub mod builtin;
12pub mod error;
13pub mod ir;
14pub mod lexer;
15pub mod lower;
16pub mod parser;
17pub mod prelude;
18pub mod program;
19pub mod schedule;
20pub mod serde_def;
21pub mod types;
22
23pub use error::{CompileError, Span};
24pub use program::RillProgram;
25pub use serde_def::{compile_def, RillLangDef};
26
27pub use builtin::{BuiltinKind, BuiltinSig, Registry, SampleBuiltin};
28
29use rill_core::math::Transcendental;
30
31/// Compile rill-lang source into a runnable [`RillProgram`] for scalar type `T`.
32///
33/// ```
34/// use rill_lang::compile;
35/// use rill_core::traits::Algorithm;
36///
37/// let mut prog = compile::<f32>("process = _ * 0.5;").unwrap();
38/// let mut out = [0.0f32; 2];
39/// prog.process(Some(&[2.0, 4.0]), &mut out).unwrap();
40/// assert_eq!(out, [1.0, 2.0]);
41/// ```
42pub fn compile<T: Transcendental>(src: &str) -> Result<RillProgram<T>, CompileError> {
43    let tokens = lexer::tokenize(src)?;
44    let program = parser::parse(&tokens)?;
45    let typed = types::infer::infer_program(&program)?;
46    let ir = lower::lower(&typed)?;
47    Ok(RillProgram::<T>::new(ir))
48}
49
50/// Compile with a built-in registry and a sample rate.
51pub fn compile_with<T: Transcendental>(
52    src: &str,
53    registry: &Registry<T>,
54    sample_rate: f32,
55) -> Result<RillProgram<T>, CompileError> {
56    let tokens = lexer::tokenize(src)?;
57    let program = parser::parse(&tokens)?;
58    let typed = types::infer::infer_program_with(&program, registry)?;
59    let ir = lower::lower_with(&typed, registry, sample_rate)?;
60    validate_block_builtins(&ir)?;
61    RillProgram::<T>::new_with(ir, registry, sample_rate)
62}
63
64fn validate_block_builtins(ir: &crate::ir::Ir) -> Result<(), CompileError> {
65    use crate::ir::Instr;
66    use crate::schedule::{build_schedule, Step};
67    for instr in &ir.instrs {
68        if let Instr::CallSample { srcs, .. } = instr {
69            if srcs.len() > backend::interp::MAX_SAMPLE_BUILTIN_INS {
70                return Err(CompileError::Unsupported(format!(
71                    "sample built-in has {} signal inputs; the maximum is {}",
72                    srcs.len(),
73                    backend::interp::MAX_SAMPLE_BUILTIN_INS,
74                )));
75            }
76        }
77    }
78    let sched = build_schedule(ir);
79    for step in &sched.steps {
80        if let Step::Sample(instrs) = step {
81            for &idx in instrs {
82                if matches!(ir.instrs[idx], Instr::CallBlock { .. }) {
83                    return Err(CompileError::Unsupported(
84                        "block built-in cannot be used inside a feedback loop (`~`)".to_string(),
85                    ));
86                }
87            }
88        }
89    }
90    Ok(())
91}