use super::{Code, Facts, Halt, R, Vm, not_yet};
use crate::abend::Abend;
use crate::arith;
use crate::fixed::{align, zoned_digits};
use crate::intrinsic::function::{self as intrinsic, Evaluator};
use crate::lir::{Argument, Base, Comparand, Const, Count, Expr, ExprId, Func, FunctionId, FunctionPlan, IntExpr, Mode, Operand, PlaceId};
use crate::storage::{Kind, Loc, Val};
use crate::store::{self, ProgramFacts};
use crate::unit::{ADDRESS_BASE, Loader};
use crate::vocab::{BinOp, Figurative, Pos};
use numeric::precision::{Fixed, Places};
use std::rc::Rc;
use zarch::decimal;
use zarch::hfp::{Hfp, Precision};
pub(super) fn constant(c: &Const) -> Val {
match c {
Const::Bytes(b) => Val::Bytes(b.clone()),
Const::National(b) => Val::National(b.clone()),
Const::Number(f) => Val::Num(*f),
Const::Figurative(f) => Val::Fig(*f),
Const::All(b) => Val::All(b.clone()),
}
}
impl<'p, L: Loader<Rc<Code>>> Vm<'p, '_, '_, L> {
pub(super) fn read(&self, loc: Loc, pos: Pos) -> R<Val> {
Ok(store::read(&self.facts(), &self.unit.mem, loc, pos)?)
}
pub(super) fn value(&mut self, o: Operand) -> R<Val> {
match o {
Operand::Load(p) => {
let loc = self.loc(p)?;
self.read(loc, self.pos(self.p.places[p as usize].at))
}
Operand::Const(c) => Ok(constant(&self.p.consts[c as usize])),
Operand::LengthOf(p) => {
let loc = self.loc(p)?;
Ok(Val::Num(Fixed::new(loc.len as i128, Places::new(9, 0))))
}
Operand::AddressOf(p) => {
if let Base::Linkage(record) = self.p.places[p as usize].base
&& self.linkage[record as usize].is_none()
{
return Ok(Val::Address(0));
}
let loc = self.loc(p)?;
Ok(Val::Address(ADDRESS_BASE + loc.offset as u32))
}
Operand::Function(f) => self.function(f),
}
}
pub(super) fn value_with_loc(&mut self, o: Operand) -> R<(Val, Option<Loc>)> {
if let Operand::Load(p) = o {
let loc = self.loc(p)?;
return Ok((self.read(loc, self.pos(self.p.places[p as usize].at))?, Some(loc)));
}
Ok((self.value(o)?, None))
}
pub(super) fn comparand(&mut self, c: &Comparand, pos: Pos) -> R<Val> {
match c {
Comparand::Operand(o) => self.value(*o),
Comparand::Expr { expr, dmax, mode, prepass } => {
for &q in prepass {
self.loc(q)?;
}
Ok(match mode {
Mode::Float(p) => Val::Float(self.eval_float(*expr, *p, pos)?),
Mode::Fixed => Val::Num(self.eval_fixed(*expr, *dmax, pos)?),
})
}
}
}
pub(super) fn comparand_with_loc(&mut self, c: &Comparand, pos: Pos) -> R<(Val, Option<Loc>)> {
match c {
Comparand::Operand(o) => self.value_with_loc(*o),
Comparand::Expr { .. } => Ok((self.comparand(c, pos)?, None)),
}
}
pub(super) fn eval_fixed(&mut self, e: ExprId, dmax: u32, pos: Pos) -> R<Fixed> {
let arith = self.p.options.options.arith;
match &self.p.exprs[e as usize] {
Expr::Operand(o) => {
let val = self.value(*o)?;
Ok(arith::fixed_operand(val, dmax, pos)?)
}
Expr::Neg(inner) => Ok(arith::fixed_neg(self.eval_fixed(*inner, dmax, pos)?)),
Expr::Bin(a, op, b) => {
let x = self.eval_fixed(*a, dmax, pos)?;
let y = self.eval_fixed(*b, dmax, pos)?;
if arith::divides_by_zero(*op, &y) {
let binary = self.binary_division(*a, *b)?;
return Err(arith::zero_divide(binary, pos).into());
}
Ok(arith::fixed_binop(x, *op, y, dmax, arith, pos)?)
}
Expr::Pow(base, exponent) => {
let x = self.eval_fixed(*base, dmax, pos)?;
let n = self.int(exponent, pos)?;
Ok(arith::pow(x, n, dmax, arith, pos)?)
}
}
}
pub(super) fn eval_float(&mut self, e: ExprId, p: Precision, pos: Pos) -> R<Hfp> {
match &self.p.exprs[e as usize] {
Expr::Operand(o) => {
let val = self.value(*o)?;
Ok(arith::float_operand(val, p, pos)?)
}
Expr::Neg(inner) => Ok(arith::float_neg(self.eval_float(*inner, p, pos)?)),
Expr::Bin(a, op, b) => {
let x = self.eval_float(*a, p, pos)?;
let y = self.eval_float(*b, p, pos)?;
Ok(arith::float_binop(x, *op, y, p, pos)?)
}
Expr::Pow(base, exponent) => {
let x = self.eval_float(*base, p, pos)?;
let y = match exponent {
IntExpr::Const(_) => x,
IntExpr::Item(q) => {
let val = self.value(Operand::Load(*q))?;
arith::float_operand(val, p, pos)?
}
IntExpr::Fixed { expr, .. } => self.eval_float(*expr, p, pos)?,
IntExpr::Walk(_) => return Err(not_yet("JSON and XML statements")),
};
Ok(arith::float_binop(x, BinOp::Pow, y, p, pos)?)
}
}
}
pub(super) fn binary_division(&self, a: ExprId, b: ExprId) -> R<bool> {
let mut items = 0;
let (x, y) = (self.binary_operands(a, &mut items)?, self.binary_operands(b, &mut items)?);
match (x, y) {
(Some(false), _) | (_, Some(false)) => Ok(false),
_ if items == 0 => Ok(false),
(Some(true), Some(true)) => Ok(true),
_ => Err(not_yet("a zero divisor beside ZERO or an integer exponent, which may have been written ALL ZERO or with a decimal point")),
}
}
fn binary_operands(&self, e: ExprId, items: &mut usize) -> R<Option<bool>> {
let binary_item = |items: &mut usize, place: PlaceId| {
let binary = matches!(self.p.places[place as usize].kind, Kind::Binary { scale: 0, .. } | Kind::Index);
*items += usize::from(binary);
binary
};
Ok(match &self.p.exprs[e as usize] {
Expr::Operand(Operand::Const(c)) => match &self.p.consts[*c as usize] {
Const::Number(f) => Some(f.places.dec == 0),
Const::Figurative(Figurative::Zero) => None,
_ => Some(false),
},
Expr::Operand(Operand::LengthOf(_)) => {
*items += 1;
Some(true)
}
Expr::Operand(Operand::Load(place)) => Some(binary_item(items, *place)),
Expr::Operand(_) => Some(false),
Expr::Neg(inner) => self.binary_operands(*inner, items)?,
Expr::Bin(x, _, y) => both(self.binary_operands(*x, items)?, self.binary_operands(*y, items)?),
Expr::Pow(x, exponent) => {
let base = self.binary_operands(*x, items)?;
let exponent = match exponent {
IntExpr::Const(_) => None,
IntExpr::Item(place) => Some(binary_item(items, *place)),
IntExpr::Fixed { expr, .. } => self.binary_operands(*expr, items)?,
IntExpr::Walk(_) => return Err(not_yet("JSON and XML statements")),
};
both(base, exponent)
}
})
}
pub(super) fn function(&mut self, id: FunctionId) -> R<Val> {
let plan = &self.p.plans.function[id as usize];
let pos = self.pos(plan.at);
let facts = self.facts();
let value = if matches!(plan.func, Func::HexOf | Func::BitOf | Func::ByteLength) {
if let Some(abend) = plan.arity {
return Err(self.abend(abend, Some(plan.at)).into());
}
let [Argument::Value(arg)] = plan.args.as_slice() else { return Err(not_yet("a storage FUNCTION without its one argument")) };
let bytes = match arg {
Comparand::Operand(Operand::Load(p)) => {
let loc = self.loc(*p)?;
store::bytes(&self.unit.mem, loc).to_vec()
}
other => {
let val = self.comparand(other, pos)?;
intrinsic::stored_bytes(&facts, val, pos)?
}
};
intrinsic::storage(&facts, plan.func.name(), &bytes, pos)?
} else {
let mut args = Vec::with_capacity(plan.args.len());
for a in &plan.args {
match a {
Argument::Value(c) => args.push(self.comparand(c, pos)?),
Argument::All { element, all } => self.all_elements(*element, all, &mut args)?,
}
}
match plan.func {
Func::Uuid4 => return Err(not_yet("FUNCTION UUID4, which gives another value on every run")),
Func::Random if self.locating > 0 => return Err(not_yet("FUNCTION RANDOM in a subscript, reference modification or OCCURS DEPENDING ON")),
_ => {}
}
let result = intrinsic::evaluate(&mut Call { vm: self, plan }, plan.func.name(), plan.side, args, pos);
self.settle(result)?
};
let Some(rm) = &plan.refmod else { return Ok(value) };
let result = intrinsic::refmod(value, pos, || {
let start = self.int(&rm.start, pos);
let start = self.lift(start, pos)?;
let length = match &rm.length {
Some(l) => {
let length = self.int(l, pos);
Some(self.lift(length, pos)?)
}
None => None,
};
Ok((start, length))
});
self.settle(result)
}
fn all_elements(&mut self, element: PlaceId, all: &[(u32, Count)], out: &mut Vec<Val>) -> R<()> {
let pos = self.pos(self.p.places[element as usize].at);
let mut counts = Vec::with_capacity(all.len());
for (_, count) in all {
counts.push(i64::from(self.count(count, pos)?));
}
if counts.contains(&0) {
return Ok(());
}
let mut current: Vec<(u32, i64)> = all.iter().map(|&(at, _)| (at, 1)).collect();
loop {
let loc = self.loc_with(element, ¤t)?;
out.push(self.read(loc, pos)?);
let mut k = current.len();
loop {
if k == 0 {
return Ok(());
}
k -= 1;
if current[k].1 < counts[k] {
current[k].1 += 1;
break;
}
current[k].1 = 1;
}
}
}
pub(super) fn content_of(&mut self, o: Operand) -> R<Vec<u8>> {
Ok(match self.value(o)? {
Val::Bytes(b) | Val::All(b) | Val::National(b) => b,
Val::Fig(f) => vec![self.facts().figurative(f)],
Val::Address(a) => a.to_be_bytes().to_vec(),
Val::Num(f) if matches!(o, Operand::LengthOf(_)) => (align(&f, 0, false).and_then(|m| m.to_u128()).unwrap_or(0) as u32).to_be_bytes().to_vec(),
Val::Num(f) => {
let digits = f.places.total().max(1);
let magnitude = align(&f, f.places.dec, false).and_then(|m| m.to_u128()).unwrap_or(0);
zoned_digits(magnitude, digits as usize, if f.negative { decimal::MINUS } else { decimal::UNSIGNED })
}
Val::Float(h) => h.to_bytes(),
})
}
pub(super) fn value_argument(&mut self, o: Operand, pos: Pos) -> R<Vec<u8>> {
Ok(match self.value(o)? {
Val::Num(f) => {
let whole = align(&f, 0, false).and_then(|m| m.to_u128()).and_then(|m| i32::try_from(m).ok());
let whole = whole.ok_or_else(|| Abend::ironwork("a BY VALUE integer beyond a fullword", pos))?;
(if f.negative { -whole } else { whole }).to_be_bytes().to_vec()
}
Val::Address(a) => a.to_be_bytes().to_vec(),
Val::Fig(Figurative::Null) => vec![0; 4],
Val::Bytes(b) => b,
_ => return Err(Abend::ironwork("this BY VALUE argument is not supported", pos).into()),
})
}
pub(super) fn program_name(&mut self, o: Operand, pos: Pos) -> R<String> {
match self.value(o)? {
Val::Bytes(b) => Ok(self.facts().page().decode(&b).trim().to_ascii_uppercase()),
_ => Err(Abend::ironwork("a program name must be alphanumeric", pos).into()),
}
}
}
fn both(x: Option<bool>, y: Option<bool>) -> Option<bool> {
match (x, y) {
(Some(false), _) | (_, Some(false)) => Some(false),
(Some(true), Some(true)) => Some(true),
_ => None,
}
}
struct Call<'a, 'p, 'u, 'w, L: Loader<Rc<Code>>> {
vm: &'a mut Vm<'p, 'u, 'w, L>,
plan: &'p FunctionPlan,
}
impl<'p, L: Loader<Rc<Code>>> Evaluator for Call<'_, 'p, '_, '_, L> {
type Facts = Facts<'p>;
fn facts(&self) -> Facts<'p> {
self.vm.facts()
}
fn integer(&mut self, _k: usize, pos: Pos) -> Result<i64, Abend> {
let value = match &self.plan.integer {
Some(e) => self.vm.int(e, pos),
None => Err(Halt::Unimplemented("a FUNCTION argument read again without its plan".into())),
};
self.vm.lift(value, pos)
}
fn written(&self) -> usize {
self.plan.args.len()
}
fn now(&self) -> (i64, u32) {
self.vm.unit.now()
}
fn compiled(&self) -> (i64, u32) {
self.vm.p.options.when_compiled.map_or((0, 0), |t| (t.seconds, t.hundredths))
}
fn random(&mut self) -> &mut Option<u32> {
&mut self.vm.unit.random
}
fn currency(&self) -> String {
self.vm.p.options.numval_currency.clone()
}
}