use super::Span;
use super::Sym;
pub(crate) type ExprList = Vec<Expr>;
#[derive(Debug, PartialEq, Eq)]
pub(crate) struct Expr {
pub kind: ExprKind,
pub span: Span,
}
#[derive(Debug, PartialEq, Eq)]
pub(crate) enum ExprKind {
Num(Num),
Ident(Sym),
Un(UnOp, Box<Expr>),
Bin(BinOp, Box<Expr>, Box<Expr>),
Cast { ty: Ty, expr: Box<Expr> },
Assign(Sym, Box<Expr>),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum UnOp {
Neg,
Not,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum BinOp {
Add,
Sub,
Mul,
Div,
Rem,
And,
Or,
Xor,
Shl,
Shr,
Pow,
}
impl UnOp {
pub fn precedence(self) -> u8 {
match self {
Self::Neg | Self::Not => 7,
}
}
}
impl BinOp {
pub fn precedence(self) -> u8 {
match self {
Self::Pow => 7,
Self::Mul | Self::Div | Self::Rem => 6,
Self::Add | Self::Sub => 5,
Self::Shl | Self::Shr => 4,
Self::And => 3,
Self::Xor => 2,
Self::Or => 1,
}
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub(crate) struct Ty {
pub signed: Option<bool>,
pub width: Option<W>,
pub width_frac: Option<W>,
}
impl Ty {
pub fn new(signed: Option<bool>, width: Option<W>, width_frac: Option<W>) -> Option<Self> {
if signed.unwrap_or(false) && matches!(width, Some(0)) {
return None;
}
if let (Some(w), Some(wf)) = (width, width_frac) {
match w.checked_add(wf) {
None | Some(0) => return None,
_ => {}
}
}
Some(Self {
signed,
width,
width_frac,
})
}
fn cast(self, ty: Ty) -> Option<Self> {
let signed = ty.signed.or(self.signed);
let width = ty.width.or(self.width);
let width_frac = ty.width_frac.or(self.width_frac);
Self::new(signed, width, width_frac)
}
pub fn s(self) -> Self {
Self::new(Some(true), self.width, self.width_frac).unwrap()
}
pub fn u(self) -> Self {
Self::new(Some(false), self.width, self.width_frac).unwrap()
}
pub fn w(self, w: W) -> Self {
Self::new(self.signed, Some(w), self.width_frac).unwrap()
}
pub fn wf(self, w: W) -> Self {
Self::new(self.signed, self.width, Some(w)).unwrap()
}
}
const SIGNED_DEFAULT: bool = true;
pub type Int = num_bigint::BigInt;
pub type Val = num_rational::Ratio<Int>;
#[cfg(not(any(test, small_max)))]
pub type W = u32;
#[cfg(any(test, small_max))]
pub type W = u8;
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct Num {
pub(crate) val: Val,
pub(crate) ty: Ty,
}
impl Num {
pub(crate) fn new(val: Val) -> Option<Self> {
if val.to_integer().bits() <= W::MAX.into() {
let ty = Ty::default();
Some(Self { val, ty })
} else {
None
}
}
#[must_use]
pub fn value(&self) -> &Val {
&self.val
}
#[must_use]
pub fn negative(&self) -> bool {
self.val < Val::default()
}
#[must_use]
pub fn signed(&self) -> bool {
self.ty.signed.unwrap_or(SIGNED_DEFAULT)
}
#[must_use]
pub fn integer(&self) -> bool {
self.val.fract() == Val::default()
}
#[must_use]
pub fn width(&self) -> Option<W> {
self.ty.width
}
#[must_use]
pub fn to_signed(&self) -> Option<Self> {
self.ty
.cast(Ty::default().s())
.map(|t| self.clone().with_ty(t))
}
#[must_use]
pub fn to_unsigned(&self) -> Self {
self.clone()
.with_ty(self.ty.cast(Ty::default().u()).unwrap())
}
pub(crate) fn cast(self, ty: Ty) -> Option<Self> {
self.ty.cast(ty).map(|ty| self.clone().with_ty(ty))
}
pub(crate) fn with_ty(self, ty: Ty) -> Self {
let val = self.val;
let val = if let Some(w) = ty.width_frac {
let denom = Int::from(1) << w;
Val::from_integer((val * Val::from_integer(denom.clone())).to_integer()) / denom
} else {
val
};
let val = if let Some(w) = ty.width {
let int = val.to_integer() & mask(w);
let int = if ty.signed.unwrap_or(SIGNED_DEFAULT)
&& w > 0
&& val.to_integer().bit(u64::from(w) - 1)
{
int - (Int::from(1) << w)
} else {
int
};
let val = Val::from_integer(int) + val.fract();
if matches!(ty.signed, Some(false)) && val < Val::default() {
Val::from_integer(Int::from(1) << w) + val
} else {
val
}
} else {
val
};
Self { val, ty }
}
}
fn mask(n: W) -> Int {
(Int::from(1) << n) - 1
}
#[cfg(test)]
mod test {
use super::Num;
use super::Ty;
use super::Val;
impl Num {
pub(crate) fn new_ty(val: Val, ty: Ty) -> Self {
Self::new(val).unwrap().with_ty(ty)
}
pub fn n(n: i128) -> Self {
Self::new_ty(Val::from_integer(n.into()), Ty::default())
}
pub fn i(n: i128) -> Self {
Self::new_ty(Val::from_integer(n.into()), Ty::default().s().wf(0))
}
pub fn u(n: i128) -> Self {
Self::new_ty(Val::from_integer(n.into()), Ty::default().u().wf(0))
}
pub fn nd(n: i128, d: u128) -> Self {
Self::new_ty(Val::new(n.into(), d.into()), Ty::default())
}
pub fn q(n: i128, d: u128) -> Self {
Self::new_ty(Val::new(n.into(), d.into()), Ty::default().s())
}
pub fn uq(n: i128, d: u128) -> Self {
Self::new_ty(Val::new(n.into(), d.into()), Ty::default().u())
}
pub fn w(self, w: super::W) -> Self {
self.cast(Ty::default().w(w)).unwrap()
}
pub fn wf(self, w: super::W) -> Self {
self.cast(Ty::default().wf(w)).unwrap()
}
}
#[test]
fn to_signed() {
assert_eq!(Num::u(4).to_signed(), Some(Num::i(4)));
assert_eq!(Num::i(4).to_signed(), Some(Num::i(4)));
assert_eq!(Num::i(-4).to_signed(), Some(Num::i(-4)));
assert_eq!(Num::u(4).w(4).to_signed(), Some(Num::i(4).w(4)));
assert_eq!(Num::u(12).w(4).to_signed(), Some(Num::i(-4).w(4)));
assert_eq!(Num::i(4).w(4).to_signed(), Some(Num::i(4).w(4)));
assert_eq!(Num::i(-4).w(4).to_signed(), Some(Num::i(-4).w(4)));
}
#[test]
fn to_signed_zero() {
assert_eq!(Num::uq(1, 2).w(0).to_signed(), None);
}
#[test]
fn to_unsigned() {
assert_eq!(Num::u(4).to_unsigned(), Num::u(4));
assert_eq!(Num::i(4).to_unsigned(), Num::u(4));
assert_eq!(Num::i(-4).to_unsigned(), Num::u(-4));
assert_eq!(Num::u(4).w(4).to_unsigned(), Num::u(4).w(4));
assert_eq!(Num::u(12).w(4).to_unsigned(), Num::u(12).w(4));
assert_eq!(Num::i(4).w(4).to_unsigned(), Num::u(4).w(4));
assert_eq!(Num::i(-4).w(4).to_unsigned(), Num::u(12).w(4));
}
#[test]
fn to_unsigned_fix() {
assert_eq!(
Num::nd(-9, 4).w(4).to_unsigned(),
Num::uq(0b1101_11, 0b1_00).w(4),
);
assert_eq!(
Num::nd(-23, 4).w(4).to_unsigned(),
Num::uq(10_25, 1_00).w(4),
);
assert_eq!(
Num::nd(-3, 4).w(3).to_unsigned(),
Num::uq(0b111_01, 0b1_00).w(3),
);
assert_eq!(
Num::nd(-0_3, 1_0).w(3).to_unsigned(),
Num::uq(15_7, 1_0).w(3),
);
}
}