#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/docs/program/README.md"))]
mod ast;
mod compile;
mod data;
mod interp;
mod parse;
mod registry;
#[cfg(test)]
mod tests;
mod value;
use std::fmt;
use std::str::FromStr;
use std::sync::{Arc, Mutex};
use crate::core::model::{Model, ModelExt};
pub use ast::{Addr, BinOp, DistCall, Expr, Program, Stmt, FORMAT_VERSION, MAX_NESTING};
pub use data::Data;
pub use registry::{Arity, HostDist, Registry, SiteType};
pub use value::Value;
#[derive(Clone, Debug, PartialEq)]
pub enum ProgramError {
Syntax {
line: usize,
message: String,
},
Json(String),
UnsupportedVersion {
found: u64,
supported: u32,
},
Check(String),
Data(String),
}
impl fmt::Display for ProgramError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ProgramError::Syntax { line, message } => write!(f, "line {line}: {message}"),
ProgramError::Json(m) => write!(f, "invalid program JSON: {m}"),
ProgramError::UnsupportedVersion { found, supported } => write!(
f,
"unsupported program format version {found}: this build of fugue reads \
version {supported}"
),
ProgramError::Check(m) | ProgramError::Data(m) => f.write_str(m),
}
}
}
impl std::error::Error for ProgramError {}
impl Program {
pub fn parse(src: &str) -> Result<Program, ProgramError> {
parse::parse_program(src)
}
pub fn compile(
&self,
registry: &Registry,
data: &Data,
) -> Result<CompiledProgram, ProgramError> {
self.validate()?;
let mut resolver = compile::Resolver::new(registry);
let mut base: Vec<Value> = Vec::with_capacity(data.len());
for (name, value) in data.iter() {
let slot = resolver.bind(name);
debug_assert_eq!(slot, base.len());
base.push(value.clone());
}
resolver.push_scope();
let body = resolver.stmts(&self.body)?;
let ret = resolver.expr(&self.ret)?;
base.resize(resolver.slot_count(), Value::F64(f64::NAN));
Ok(CompiledProgram {
shared: Arc::new(interp::Shared {
body: body.into(),
ret,
warnings: Mutex::new(Vec::new()),
}),
base: base.into(),
})
}
}
impl FromStr for Program {
type Err = ProgramError;
fn from_str(s: &str) -> Result<Program, ProgramError> {
Program::parse(s)
}
}
#[derive(Clone)]
pub struct CompiledProgram {
shared: Arc<interp::Shared>,
base: Arc<[Value]>,
}
impl fmt::Debug for CompiledProgram {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("CompiledProgram")
.field("statements", &self.shared.body.len())
.field("slots", &self.base.len())
.finish_non_exhaustive()
}
}
impl CompiledProgram {
pub fn build(&self) -> Model<Value> {
interp::start(self.shared.clone(), &self.base)
}
pub fn build_f64(&self) -> Model<f64> {
self.build().map(|v| v.to_f64_lossy())
}
pub fn take_warnings(&self) -> Vec<String> {
self.shared
.warnings
.lock()
.map(|mut w| std::mem::take(&mut *w))
.unwrap_or_default()
}
}