ecore-macro 0.10.0

Procedural macros for ecore: bitfield structs/enums, enum maps, and ranged integers
Documentation
use std::{
    fmt::{Debug, Display},
    ops::{Add, Div, Rem, Sub},
    str::FromStr,
};

use quote::{format_ident, quote};

use crate::error;

pub fn rint(item: proc_macro::TokenStream) -> syn::Result<proc_macro2::TokenStream> {
    let lit: LitRInt = syn::parse(item)?;
    let neg = lit.min.base10_digits().starts_with('-') || lit.max.base10_digits().starts_with('-');
    if neg {
        if let Ok(min) = lit.min.base10_parse::<isize>()
            && let Ok(max) = lit.max.base10_parse::<isize>()
        {
            RInt { min, max }.gen_def(lit, neg)
        } else {
            let min = lit.min.base10_parse::<i128>()?;
            let max = lit.max.base10_parse::<i128>()?;
            RInt { min, max }.gen_def(lit, neg)
        }
    } else {
        if let Ok(min) = lit.min.base10_parse::<usize>()
            && let Ok(max) = lit.max.base10_parse::<usize>()
        {
            RInt { min, max }.gen_def(lit, neg)
        } else {
            let min = lit.min.base10_parse::<u128>()?;
            let max = lit.max.base10_parse::<u128>()?;
            RInt { min, max }.gen_def(lit, neg)
        }
    }
}

struct LitRInt {
    min: syn::LitInt,
    range_limits: syn::RangeLimits,
    max: syn::LitInt,
    default: Option<syn::LitInt>,
    step: Option<syn::LitInt>,
    bits: Option<syn::LitInt>,
}

impl syn::parse::Parse for LitRInt {
    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
        let min = input.parse()?;
        let range_limits = input.parse()?;
        let max = input.parse()?;
        let mut default = None;
        let mut step = None;
        let mut bits = None;
        while input.parse::<syn::Token![,]>().is_ok() {
            let false = input.is_empty() else { break };
            let name: syn::Ident = input.parse()?;
            input.parse::<syn::Token![=]>()?;
            if name == "default" {
                let None = default.replace(input.parse()?) else { return error(name.span(), "already setted") };
            } else if name == "step" {
                let None = step.replace(input.parse()?) else { return error(name.span(), "already setted") };
            } else if name == "bits" {
                let None = bits.replace(input.parse()?) else { return error(name.span(), "already setted") };
            } else {
                return error(name.span(), "Unknown property");
            }
        }
        Ok(Self { min, range_limits, max, default, step, bits })
    }
}

struct RInt<T> {
    min: T,
    max: T,
}

impl<T: ParseInt> RInt<T> {
    fn gen_def(self, lit: LitRInt, is_neg: bool) -> syn::Result<proc_macro2::TokenStream> {
        let Self { min, max } = self;
        let max = match lit.range_limits {
            syn::RangeLimits::HalfOpen(_) if min < max => max - T::ONE,
            syn::RangeLimits::Closed(_) if min <= max => max,
            _ => return error(lit.max.span(), "max must not less than min"),
        };
        let (step, min_bits) = if let Some(value) = lit.step {
            let step: T = value.base10_parse()?;
            let true = step > T::ZERO else { return error(value.span(), "step must >= 1") };
            let true = min % step == T::ZERO else { return error(lit.min.span(), "not muliple of step") };
            let true = max % step == T::ZERO else { return error(lit.max.span(), format!("{} not muliple of step", max)) };
            (if step > T::ONE { Some(step) } else { None }, (max.abs_diff(min) / step.cast_unsigned()).bit_width())
        } else {
            (None, max.abs_diff(min).bit_width())
        };
        let default = if let Some(value) = lit.default {
            let default: T = value.base10_parse()?;
            let true = (min <= default && default <= max) else { return error(value.span(), format!("default must in setted range: {}..={}", min, max)) };
            let true = (step.is_none() || step.is_some_and(|step| default % step == T::ZERO)) else { return error(value.span(), "not muliple of step") };
            Some(default)
        } else {
            None
        };
        let bits = if let Some(value) = lit.bits {
            let bits = value.base10_parse()?;
            let true = min_bits <= bits else { return error(value.span(), format!("bits underflow, valid min is {}", min_bits)) };
            let true = bits <= 128 else { return error(value.span(), "bits overflow, valid max is 128") };
            bits
        } else {
            min_bits
        };

        let repr = format_ident!("u{}", bits);
        let rr = format_ident!("RR{}{}", if is_neg { 'I' } else { 'U' }, min.bit_width().max(max.bit_width()).next_multiple_of(8).next_power_of_two());
        let tail = if let Some(step) = step {
            let default = default.unwrap_or(min).unsuffixed();
            let step = step.unsuffixed();
            quote!(, #default, #step)
        } else if let Some(default) = default {
            let default = default.unsuffixed();
            quote!(, #default)
        } else {
            quote!()
        };
        let min = min.unsuffixed();
        let max = max.unsuffixed();
        Ok(quote!(RInt<#repr, #rr<#min, #max #tail>>))
    }
}

trait ParseInt: FromStr<Err: Display> + Ord + Sub<Output = Self> + Add<Output = Self> + Copy + Rem<Output = Self> + Div<Output = Self> + Debug + Display {
    type Unsigned: ParseInt;
    const ONE: Self;
    const ZERO: Self;
    fn unsuffixed(self) -> proc_macro2::Literal;
    fn bit_width(self) -> u32;
    fn abs_diff(self, other: Self) -> Self::Unsigned;
    fn cast_unsigned(self) -> Self::Unsigned;
}

macro_rules! impl_parseint {
    ($signed:ty,$unsigned:ty, $sop:ident, $uop:ident) => {
        impl ParseInt for $signed {
            type Unsigned = $unsigned;
            const ONE: Self = 1;
            const ZERO: Self = 0;

            fn unsuffixed(self) -> proc_macro2::Literal {
                proc_macro2::Literal::$sop(self)
            }

            fn bit_width(self) -> u32 {
                let true = self != 0 else { return 1 };
                if self < 0 { (const { Self::BITS + 1 }) - self.leading_ones() } else { Self::BITS - self.leading_zeros() }
            }

            fn abs_diff(self, other: Self) -> Self::Unsigned {
                self.abs_diff(other)
            }

            fn cast_unsigned(self) -> Self::Unsigned {
                self.cast_unsigned()
            }
        }

        impl ParseInt for $unsigned {
            type Unsigned = Self;
            const ONE: Self = 1;
            const ZERO: Self = 0;

            fn unsuffixed(self) -> proc_macro2::Literal {
                proc_macro2::Literal::$uop(self)
            }

            fn bit_width(self) -> u32 {
                if self == 0 { 1 } else { Self::BITS - self.leading_zeros() }
            }

            fn abs_diff(self, other: Self) -> Self::Unsigned {
                self.abs_diff(other)
            }

            fn cast_unsigned(self) -> Self::Unsigned {
                self
            }
        }
    };
}
impl_parseint!(isize, usize, isize_unsuffixed, usize_unsuffixed);
impl_parseint!(i128, u128, i128_unsuffixed, u128_unsuffixed);