use super::*;
#[derive(Debug)]
pub struct ImlSequence {
consuming: Option<Consumable>, items: Vec<ImlOp>,
}
impl ImlSequence {
pub fn new(items: Vec<ImlOp>) -> ImlOp {
Self {
consuming: None,
items,
}
.into_op()
}
}
impl Compileable for ImlSequence {
fn resolve(&mut self, usages: &mut Vec<Vec<ImlOp>>) {
let mut end = self.items.len();
let mut i = 0;
while i < end {
let item = self.items.get_mut(i).unwrap();
if let ImlOp::Usage(usage) = *item {
let n = usages[usage].len();
self.items.splice(i..i + 1, usages[usage].drain(..));
i += n;
end = self.items.len();
} else {
i += 1
}
}
for item in self.items.iter_mut() {
item.resolve(usages);
}
}
fn finalize(
&mut self,
values: &Vec<ImlValue>,
stack: &mut Vec<(usize, bool)>,
) -> Option<Consumable> {
let mut leftrec = false;
let mut nullable = true;
let mut consumes = false;
for item in self.items.iter_mut() {
if !nullable {
break;
}
if let Some(consumable) = item.finalize(values, stack) {
leftrec |= consumable.leftrec;
nullable = consumable.nullable;
consumes = true;
}
}
if stack.len() == 1 && consumes {
self.consuming = Some(Consumable { leftrec, nullable });
}
if consumes {
Some(Consumable { leftrec, nullable })
} else {
None
}
}
fn compile(&self, parselet: &ImlParselet) -> Vec<Op> {
let mut ret = Vec::new();
for item in self.items.iter() {
let mut ops = item.compile(parselet);
match ops.last().unwrap() {
Op::UnaryOp(op) | Op::BinaryOp(op) if op.starts_with("i") => {
ops.push(Op::Sep);
}
_ => {}
}
ret.extend(ops);
}
if ret
.iter()
.map(|op| if matches!(op, Op::Offset(_)) { 0 } else { 1 })
.sum::<usize>()
> 1
{
ret.insert(0, Op::Frame(0));
ret.push(Op::Collect(0, 5));
ret.push(Op::Close);
}
ret
}
}
impl std::fmt::Display for ImlSequence {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "(")?;
for item in &self.items {
write!(f, "{} ", item)?;
}
write!(f, ")")?;
Ok(())
}
}