use super::value::constant;
use super::{Code, R, Vm, not_yet};
use crate::lir::{Comparand, Compare, Cond, CondId, Const, Operand, PlaceId};
use crate::oo;
use crate::storage::{Kind, Loc, Val};
use crate::store::{self, ProgramFacts};
use crate::unit::Loader;
use crate::vocab::{Figurative, Pos, RelOp};
use std::cmp::Ordering;
use std::rc::Rc;
fn holds(op: RelOp, o: Ordering) -> bool {
match op {
RelOp::Eq => o == Ordering::Equal,
RelOp::Ne => o != Ordering::Equal,
RelOp::Lt => o == Ordering::Less,
RelOp::Le => o != Ordering::Greater,
RelOp::Gt => o == Ordering::Greater,
RelOp::Ge => o != Ordering::Less,
}
}
impl<L: Loader<Rc<Code>>> Vm<'_, '_, '_, L> {
pub(super) fn cond(&mut self, c: CondId, pos: Pos) -> R<bool> {
let p = self.p;
Ok(match &p.conds[c as usize] {
Cond::Rel { a, op, b, how } => holds(*op, self.compare(a, b, *how, pos)?),
Cond::Class { place, test } => {
let loc = self.loc(*place)?;
store::byte_class(&self.facts(), &self.unit.mem, loc, *test)
}
Cond::Sign { value, test } => {
let v = self.comparand(value, pos)?;
store::sign_test(v, *test, pos)?
}
Cond::Name { subject, values, .. } => {
let loc = self.loc(*subject)?;
let subject = (self.read(loc, self.pos(p.places[*subject as usize].at))?, Some(loc));
for (low, high) in values {
let hit = match high {
None => self.compare_constant(&subject, *low, pos)? == Ordering::Equal,
Some(high) => self.compare_constant(&subject, *low, pos)? != Ordering::Less && self.compare_constant(&subject, *high, pos)? != Ordering::Greater,
};
if hit {
return Ok(true);
}
}
false
}
Cond::Not(inner) => !self.cond(*inner, pos)?,
Cond::And(a, b) => self.cond(*a, pos)? && self.cond(*b, pos)?,
Cond::Or(a, b) => self.cond(*a, pos)? || self.cond(*b, pos)?,
Cond::Counter(t) => self.returns.frames.last().and_then(|f| f.temps.get(usize::from(*t))).is_some_and(|&n| n > 0),
Cond::InTable { index, count } => {
let count = i64::from(self.count(count, pos)?);
let i = self.int_place(*index, pos)?;
(1..=count).contains(&i)
}
Cond::Sql(_) => return Err(not_yet("EXEC SQL")),
})
}
fn compare_constant(&self, subject: &(Val, Option<Loc>), c: u32, pos: Pos) -> R<Ordering> {
let value = constant(&self.p.consts[c as usize]);
Ok(store::compare(&self.facts(), &self.unit.mem, subject.clone(), (value, None), pos)?)
}
pub(super) fn compare(&mut self, a: &Comparand, b: &Comparand, how: Compare, pos: Pos) -> R<Ordering> {
let mut zoned = self.zoned_against(a, b)?;
if how != (Compare::ZonedBytes { zoned_first: true }) {
zoned = self.zoned_against(b, a)?;
}
if let Compare::ZonedBytes { zoned_first } = how {
let (side, other) = if zoned_first { (a, b) } else { (b, a) };
let Comparand::Operand(Operand::Load(place)) = side else { return Err(not_yet("zoned bytes of a side that is not a data item")) };
let loc = match zoned {
Some(loc) => loc,
None => self.loc(*place)?,
};
let facts = self.facts();
let Some(image) = store::compared_zoned_bytes(&facts, &self.unit.mem, loc) else { return Err(not_yet("zoned bytes of an item that is not a zoned integer")) };
let other = match other {
Comparand::Operand(Operand::Load(q)) if self.zone_sensitive(*q)? => {
let loc = self.loc(*q)?;
(Val::Bytes(Vec::new()), Some(loc))
}
_ => self.comparand_with_loc(other, pos)?,
};
return Ok(store::compare_zoned_bytes(&facts, &self.unit.mem, &image, other, zoned_first, pos)?);
}
let (va, la) = self.comparand_with_loc(a, pos)?;
let (vb, lb) = self.comparand_with_loc(b, pos)?;
let names = || (self.operand_name(a), self.operand_name(b));
if let Some(o) = oo::compare_references(&self.unit.oo, names, (&va, la), (&vb, lb), pos)? {
return Ok(o);
}
Ok(store::compare(&self.facts(), &self.unit.mem, (va, la), (vb, lb), pos)?)
}
fn operand_name(&self, c: &Comparand) -> String {
match c {
Comparand::Operand(Operand::Load(p)) => self.sym(self.p.places[*p as usize].name).to_owned(),
_ => String::new(),
}
}
fn zoned_against(&mut self, e: &Comparand, other: &Comparand) -> R<Option<Loc>> {
let Comparand::Operand(Operand::Load(r)) = e else { return Ok(None) };
let nonnumeric = match other {
Comparand::Operand(Operand::Const(c)) => match &self.p.consts[*c as usize] {
Const::Bytes(_) | Const::All(_) => true,
Const::Figurative(f) => !matches!(f, Figurative::Zero | Figurative::Null),
Const::National(_) | Const::Number(_) => false,
},
Comparand::Operand(Operand::Load(o)) => {
let kind = self.loc(*o)?.kind;
matches!(kind, Kind::Group | Kind::Alnum { .. } | Kind::AlnumEdited { .. } | Kind::NumericEdited { .. })
}
_ => false,
};
let options = self.p.options.options;
let zones_count = options.invdata.is_some_and(|i| !i.forcenumcmp)
&& self.zone_sensitive(*r)?
&& match other {
Comparand::Operand(Operand::Const(c)) => matches!(self.p.consts[*c as usize], Const::Figurative(Figurative::Zero)),
Comparand::Operand(Operand::Load(o)) => self.zone_sensitive(*o)? && self.loc(*o)?.len == self.loc(*r)?.len,
_ => false,
};
if !nonnumeric && !zones_count {
return Ok(None);
}
let loc = self.loc(*r)?;
Ok(store::compared_zoned_bytes(&self.facts(), &self.unit.mem, loc).map(|_| loc))
}
fn zone_sensitive(&mut self, place: PlaceId) -> R<bool> {
let loc = self.loc(place)?;
Ok(matches!(loc.kind, Kind::Zoned { scale: 0, signed: false, .. }) && self.facts().scaling(loc.item) == 0)
}
}