use serde::{Deserialize, Serialize};
use super::op::Op;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Program {
ops: Vec<Op>,
}
impl Program {
pub fn new(ops: Vec<Op>) -> Self {
Self { ops }
}
pub fn ops(&self) -> &[Op] {
&self.ops
}
pub fn len(&self) -> usize {
self.ops.len()
}
pub fn is_empty(&self) -> bool {
self.ops.is_empty()
}
pub fn stack_depth(&self) -> usize {
let mut depth: i64 = 0;
let mut max_depth: i64 = 0;
for op in &self.ops {
let (pop, push) = op.stack_effect();
depth -= pop as i64;
depth += push as i64;
if depth > max_depth {
max_depth = depth;
}
}
max_depth as usize
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::iteration::comprehension::source::Source;
use crate::iteration::comprehension::strategy::ZipMode;
fn push_clause(name: &str) -> Op {
Op::PushClause {
name: name.into(),
source: Source::Literal { values: vec![] },
}
}
#[test]
fn stack_depth_simple() {
let p = Program::new(vec![
push_clause("a"),
push_clause("b"),
Op::Cartesian { n: 2 },
Op::Dispense,
]);
assert_eq!(p.stack_depth(), 2);
}
#[test]
fn stack_depth_nested() {
let p = Program::new(vec![
push_clause("a"),
push_clause("b"),
push_clause("c"),
Op::Cartesian { n: 3 },
Op::Dispense,
]);
assert_eq!(p.stack_depth(), 3);
}
#[test]
fn stack_depth_zip_then_cartesian() {
let p = Program::new(vec![
push_clause("a"),
push_clause("b"),
Op::Zip { n: 2, mode: ZipMode::Strict },
push_clause("c"),
Op::Cartesian { n: 2 },
Op::Dispense,
]);
assert_eq!(p.stack_depth(), 2);
}
#[test]
fn round_trip_serde() {
let p = Program::new(vec![
push_clause("a"),
Op::Dispense,
]);
let json = serde_json::to_string(&p).unwrap();
let back: Program = serde_json::from_str(&json).unwrap();
assert_eq!(p, back);
}
}