moore-vhdl 0.11.0

The VHDL implementation of the moore compiler framework.
Documentation
// Copyright (c) 2016-2020 Fabian Schuiki

//! Physical types.

use std::fmt::{self, Display};
use std::iter::{once, repeat};
use std::ops::Deref;

pub use num::BigInt;

use crate::common::name::Name;
use crate::ty2::prelude::*;
use crate::ty2::ScalarSubtype;

/// A physical type.
///
/// This can either be an `PhysicalBasetype` or a `PhysicalSubtype`.
pub trait PhysicalType: Type {
    /// Convert to a type.
    fn as_type(&self) -> &Type;

    /// The range of values this physical type can assume.
    fn range(&self) -> &Range<BigInt>;

    /// The units of measure of this type.
    fn units(&self) -> &[PhysicalUnit];

    /// The index of the primary unit.
    fn primary_index(&self) -> usize;

    /// The base type of this physical type.
    fn base_type(&self) -> &Type;

    /// The resolution function associated with this type.
    fn resolution_func(&self) -> Option<usize> {
        None
    }

    /// Returns `Some` if self is a `PhysicalBasetype`, `None` otherwise.
    fn as_basetype(&self) -> Option<&PhysicalBasetype> {
        None
    }

    /// Returns `Some` if self is a `PhysicalSubtype`, `None` otherwise.
    fn as_subtype(&self) -> Option<&PhysicalSubtype> {
        None
    }

    /// Returns an `&PhysicalBasetype` or panics if the type is not a basetype.
    fn unwrap_basetype(&self) -> &PhysicalBasetype {
        self.as_basetype().expect("physical type is not a basetype")
    }

    /// Returns an `&PhysicalSubtype` or panics if the type is not a subtype.
    fn unwrap_subtype(&self) -> &PhysicalSubtype {
        self.as_subtype().expect("physical type is not a subtype")
    }

    /// Check if two physical types are equal.
    fn is_equal(&self, other: &PhysicalType) -> bool;
}

impl<'t> PartialEq for PhysicalType + 't {
    fn eq(&self, other: &PhysicalType) -> bool {
        PhysicalType::is_equal(self, other)
    }
}

impl<'t> Eq for PhysicalType + 't {}

macro_rules! common_type_impl {
    () => {
        fn is_scalar(&self) -> bool {
            true
        }

        fn is_discrete(&self) -> bool {
            false
        }

        fn is_numeric(&self) -> bool {
            true
        }

        fn is_composite(&self) -> bool {
            false
        }

        fn as_any(&self) -> AnyType {
            AnyType::Physical(self)
        }
    };
}

/// A physical base type.
///
/// In VHDL a physical type is an integer multiple of some measurement unit.
/// A physical type has exactly one primary unit, and multiple secondary units
/// defined as multiples of that primary unit.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PhysicalBasetype {
    /// The range of integer multiples of the primary unit.
    range: Range<BigInt>,
    /// The units of this type.
    units: Vec<PhysicalUnit>,
    /// The index of the primary unit.
    primary: usize,
}

impl PhysicalBasetype {
    /// Create a new physical type.
    ///
    /// # Example
    ///
    /// ```
    /// use moore_vhdl::ty2::{PhysicalBasetype, PhysicalUnit, Range};
    /// use moore_common::name::get_name_table;
    ///
    /// let ty = PhysicalBasetype::new(Range::ascending(0, 1_000_000), vec![
    ///     PhysicalUnit::primary(get_name_table().intern("fs", false), 1),
    ///     PhysicalUnit::secondary(get_name_table().intern("ps", false), 1_000, 1000, 0),
    ///     PhysicalUnit::secondary(get_name_table().intern("ns", false), 1_000_000, 1000, 1),
    /// ], 0);
    ///
    /// assert_eq!(format!("{}", ty), "0 to 1000000 units (fs, ps, ns)");
    /// ```
    pub fn new<I>(range: Range<BigInt>, units: I, primary: usize) -> PhysicalBasetype
    where
        I: IntoIterator<Item = PhysicalUnit>,
    {
        PhysicalBasetype {
            range: range,
            units: units.into_iter().collect(),
            primary: primary,
        }
    }
}

impl Type for PhysicalBasetype {
    common_type_impl!();

    fn into_owned<'a>(self) -> OwnedType<'a>
    where
        Self: 'a,
    {
        OwnedType::PhysicalBasetype(self)
    }

    fn to_owned<'a>(&self) -> OwnedType<'a>
    where
        Self: 'a,
    {
        OwnedType::PhysicalBasetype(self.clone())
    }
}

impl PhysicalType for PhysicalBasetype {
    fn as_type(&self) -> &Type {
        self
    }

    fn range(&self) -> &Range<BigInt> {
        &self.range
    }

    fn units(&self) -> &[PhysicalUnit] {
        &self.units
    }

    fn primary_index(&self) -> usize {
        self.primary
    }

    fn base_type(&self) -> &Type {
        self
    }

    fn as_basetype(&self) -> Option<&PhysicalBasetype> {
        Some(self)
    }

    fn is_equal(&self, other: &PhysicalType) -> bool {
        other.as_basetype().map(|t| self == t).unwrap_or(false)
    }
}

impl Display for PhysicalBasetype {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} units (", self.range)?;
        for (sep, unit) in once("").chain(repeat(", ")).zip(self.units.iter()) {
            write!(f, "{}{}", sep, unit.name)?;
        }
        write!(f, ")")?;
        Ok(())
    }
}

impl Deref for PhysicalBasetype {
    type Target = Range<BigInt>;
    fn deref(&self) -> &Range<BigInt> {
        &self.range
    }
}

/// A subtype of an integer type.
pub type PhysicalSubtype<'t> = ScalarSubtype<'t, PhysicalType, BigInt>;

impl<'t> PhysicalSubtype<'t> {
    /// Create a new integer subtype.
    ///
    /// Returns `Some(...)` if `range` is a subrange of the integer, or `None`
    /// otherwise.
    ///
    /// # Example
    ///
    /// ```
    /// use moore_vhdl::ty2::{Type, TypeMark, PhysicalUnit, PhysicalBasetype, PhysicalSubtype, Range};
    /// use moore_common::name::get_name_table;
    ///
    /// let ty = PhysicalBasetype::new(Range::ascending(-1000isize, 1000isize), vec![
    ///     PhysicalUnit::primary(get_name_table().intern("fs", false), 1),
    ///     PhysicalUnit::secondary(get_name_table().intern("ps", false), 1000, 1000, 0),
    /// ], 0);
    /// let tm = TypeMark::new(
    ///     get_name_table().intern("TIME", false),
    ///     &ty,
    /// );
    /// let a = PhysicalSubtype::new(&tm, Range::ascending(0isize, 100isize)).unwrap();
    /// let b = PhysicalSubtype::new(&tm, Range::descending(100isize, 0isize)).unwrap();
    ///
    /// assert_eq!(format!("{}", a), "TIME range 0 to 100");
    /// assert_eq!(format!("{}", b), "TIME range 100 downto 0");
    /// ```
    pub fn new(mark: &'t TypeMark<'t>, range: Range<BigInt>) -> Option<PhysicalSubtype<'t>> {
        let base = mark.as_any().as_physical()?;
        let base_range = base.range();
        if base_range.has_subrange(&range) {
            Some(PhysicalSubtype {
                resfn: base.resolution_func(),
                mark: mark,
                base: base,
                con: range,
            })
        } else {
            None
        }
    }
}

impl<'t> Type for PhysicalSubtype<'t> {
    common_type_impl!();

    fn into_owned<'a>(self) -> OwnedType<'a>
    where
        Self: 'a,
    {
        OwnedType::PhysicalSubtype(self)
    }

    fn to_owned<'a>(&self) -> OwnedType<'a>
    where
        Self: 'a,
    {
        OwnedType::PhysicalSubtype(self.clone())
    }
}

impl<'t> PhysicalType for PhysicalSubtype<'t> {
    fn as_type(&self) -> &Type {
        self
    }

    fn range(&self) -> &Range<BigInt> {
        &self.con
    }

    fn units(&self) -> &[PhysicalUnit] {
        self.base.units()
    }

    fn primary_index(&self) -> usize {
        self.base.primary_index()
    }

    fn base_type(&self) -> &Type {
        self.base.as_type()
    }

    fn as_subtype(&self) -> Option<&PhysicalSubtype> {
        Some(self)
    }

    fn is_equal(&self, other: &PhysicalType) -> bool {
        other.as_subtype().map(|t| self == t).unwrap_or(false)
    }
}

impl<'t> Display for PhysicalSubtype<'t> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} range {}", self.mark, self.con)
    }
}

/// A unit of a physical type.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PhysicalUnit {
    /// The name of the unit.
    pub name: Name,
    /// The scale of the unit with respect to the physical type's primary unit.
    pub abs: BigInt,
    /// The scale of the unit with respect to another unit.
    pub rel: Option<(BigInt, usize)>,
}

impl PhysicalUnit {
    /// Create a new unit.
    ///
    /// # Example
    ///
    /// ```
    /// use moore_vhdl::ty2::{PhysicalUnit, BigInt};
    /// use moore_common::name::get_name_table;
    ///
    /// let name = get_name_table().intern("fs", false);
    /// let unit = PhysicalUnit::new(name, 1, Some((1000, 0)));
    ///
    /// assert_eq!(unit.name, name);
    /// assert_eq!(unit.abs, BigInt::from(1));
    /// assert_eq!(unit.rel, Some((BigInt::from(1000), 0)));
    /// ```
    pub fn new<A, R>(name: Name, abs: A, rel: Option<(R, usize)>) -> PhysicalUnit
    where
        BigInt: From<A> + From<R>,
    {
        PhysicalUnit {
            name: name,
            abs: abs.into(),
            rel: rel.map(|(rel, rel_to)| (rel.into(), rel_to)),
        }
    }

    /// Create a new primary unit.
    ///
    /// # Example
    ///
    /// ```
    /// use moore_vhdl::ty2::{PhysicalUnit, BigInt};
    /// use moore_common::name::get_name_table;
    ///
    /// let name = get_name_table().intern("fs", false);
    /// let unit = PhysicalUnit::primary(name, 1);
    ///
    /// assert_eq!(unit.name, name);
    /// assert_eq!(unit.abs, BigInt::from(1));
    /// assert_eq!(unit.rel, None);
    /// ```
    pub fn primary<A>(name: Name, abs: A) -> PhysicalUnit
    where
        BigInt: From<A>,
    {
        PhysicalUnit {
            name: name,
            abs: abs.into(),
            rel: None,
        }
    }

    /// Create a new secondary unit.
    ///
    /// # Example
    ///
    /// ```
    /// use moore_vhdl::ty2::{PhysicalUnit, BigInt};
    /// use moore_common::name::get_name_table;
    ///
    /// let name = get_name_table().intern("fs", false);
    /// let unit = PhysicalUnit::secondary(name, 1, 1000, 0);
    ///
    /// assert_eq!(unit.name, name);
    /// assert_eq!(unit.abs, BigInt::from(1));
    /// assert_eq!(unit.rel, Some((BigInt::from(1000), 0)));
    /// ```
    pub fn secondary<A, R>(name: Name, abs: A, rel: R, rel_to: usize) -> PhysicalUnit
    where
        BigInt: From<A> + From<R>,
    {
        PhysicalUnit {
            name: name,
            abs: abs.into(),
            rel: Some((rel.into(), rel_to)),
        }
    }
}