use calyx_utils::{CalyxResult, Error, Id};
use std::str::FromStr;
use strum::EnumCount;
use strum_macros::{AsRefStr, EnumCount, EnumString, FromRepr};
pub const DEPRECATED_ATTRIBUTES: &[&str] = &["static"];
#[derive(
EnumCount,
FromRepr,
AsRefStr,
EnumString,
Clone,
Copy,
Hash,
PartialEq,
Eq,
Debug,
)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
#[repr(u8)]
pub enum BoolAttr {
#[strum(serialize = "toplevel")]
TopLevel,
#[strum(serialize = "external")]
External,
#[strum(serialize = "nointerface")]
NoInterface,
#[strum(serialize = "reset")]
Reset,
#[strum(serialize = "clk")]
Clk,
#[strum(serialize = "stable")]
Stable,
#[strum(serialize = "data")]
Data,
#[strum(serialize = "control")]
Control,
#[strum(serialize = "share")]
Share,
#[strum(serialize = "state_share")]
StateShare,
#[strum(serialize = "generated")]
Generated,
#[strum(serialize = "new_fsm")]
NewFSM,
#[strum(serialize = "one_hot")]
OneHot,
#[strum(serialize = "inline")]
Inline,
#[strum(serialize = "promoted")]
Promoted,
#[strum(serialize = "par")]
ParCtrl,
#[strum(serialize = "fast")]
Fast,
#[strum(serialize = "protected")]
Protected,
}
impl From<BoolAttr> for Attribute {
fn from(attr: BoolAttr) -> Self {
Attribute::Bool(attr)
}
}
impl std::fmt::Display for BoolAttr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_ref())
}
}
#[derive(AsRefStr, EnumString, Clone, Copy, Hash, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
pub enum NumAttr {
#[strum(serialize = "go")]
Go,
#[strum(serialize = "done")]
Done,
#[strum(serialize = "read_together")]
ReadTogether,
#[strum(serialize = "write_together")]
WriteTogether,
#[strum(serialize = "sync")]
Sync,
#[strum(serialize = "bound")]
Bound,
#[strum(serialize = "promotable")]
Promotable,
#[strum(serialize = "compactable")]
Compactable,
#[strum(serialize = "interval")]
Interval,
}
impl From<NumAttr> for Attribute {
fn from(attr: NumAttr) -> Self {
Attribute::Num(attr)
}
}
impl std::fmt::Display for NumAttr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_ref())
}
}
#[derive(AsRefStr, Clone, Copy, Hash, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
#[allow(non_camel_case_types)]
pub enum InternalAttr {
DEAD,
NODE_ID,
BEGIN_ID,
END_ID,
ST_ID,
LOOP,
START,
END,
}
impl From<InternalAttr> for Attribute {
fn from(attr: InternalAttr) -> Self {
Attribute::Internal(attr)
}
}
#[derive(
AsRefStr,
EnumString,
Clone,
Copy,
Hash,
PartialEq,
Eq,
Debug,
strum_macros::Display,
PartialOrd,
Ord,
)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
pub enum SetAttr {
#[strum(serialize = "pos")]
Pos,
}
#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
pub enum SetAttribute {
Set(SetAttr),
Unknown(Id),
}
impl Ord for SetAttribute {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
match (self, other) {
(SetAttribute::Set(s1), SetAttribute::Set(s2)) => s1.cmp(s2),
(SetAttribute::Set(_), SetAttribute::Unknown(_)) => {
std::cmp::Ordering::Less
}
(SetAttribute::Unknown(_), SetAttribute::Set(_)) => {
std::cmp::Ordering::Greater
}
(SetAttribute::Unknown(id1), SetAttribute::Unknown(id2)) => {
id1.as_ref().cmp(id2.as_ref())
}
}
}
}
impl PartialOrd for SetAttribute {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl From<Id> for SetAttribute {
fn from(v: Id) -> Self {
Self::Unknown(v)
}
}
impl From<SetAttr> for SetAttribute {
fn from(v: SetAttr) -> Self {
Self::Set(v)
}
}
impl std::fmt::Display for SetAttribute {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SetAttribute::Set(set_attr) => set_attr.fmt(f),
SetAttribute::Unknown(id) => id.fmt(f),
}
}
}
impl FromStr for SetAttribute {
type Err = Error;
fn from_str(s: &str) -> CalyxResult<Self> {
if let Ok(s) = SetAttr::from_str(s) {
Ok(SetAttribute::Set(s))
} else {
if s.to_uppercase() == s {
return Err(Error::misc(format!(
"Invalid attribute: {}. All caps attributes are reserved for internal use.",
s
)));
}
Ok(SetAttribute::Unknown(s.into()))
}
}
}
#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
pub enum Attribute {
Bool(BoolAttr),
Num(NumAttr),
Internal(InternalAttr),
Unknown(Id),
}
impl std::fmt::Display for Attribute {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Attribute::Bool(b) => write!(f, "{}", b.as_ref()),
Attribute::Num(n) => write!(f, "{}", n.as_ref()),
Attribute::Internal(i) => write!(f, "{}", i.as_ref()),
Attribute::Unknown(s) => write!(f, "{}", s),
}
}
}
impl FromStr for Attribute {
type Err = Error;
fn from_str(s: &str) -> CalyxResult<Self> {
if let Ok(b) = BoolAttr::from_str(s) {
Ok(Attribute::Bool(b))
} else if let Ok(n) = NumAttr::from_str(s) {
Ok(Attribute::Num(n))
} else {
if DEPRECATED_ATTRIBUTES.contains(&s) {
log::warn!(
"The attribute @{s} is deprecated and will be ignored by the compiler."
);
}
if let Ok(SetAttribute::Set(_)) = SetAttribute::from_str(s) {
log::warn!(
"Set attribute {s} incorrectly written as a standard attribute, i.e. '@{s}(..)' or '\"{s}\" = ..'. This will be ignored by the compiler. Instead write '@{s}{{..}}' or '\"{s}\" = {{..}}'."
);
}
if s.to_uppercase() == s {
return Err(Error::misc(format!(
"Invalid attribute: {}. All caps attributes are reserved for internal use.",
s
)));
}
Ok(Attribute::Unknown(s.into()))
}
}
}
#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub(super) struct InlineAttributes {
attrs: u32,
}
impl InlineAttributes {
pub const fn is_empty(&self) -> bool {
self.attrs == 0
}
pub fn insert(&mut self, attr: BoolAttr) {
self.attrs |= 1 << attr as u8;
}
pub fn has(&self, attr: BoolAttr) -> bool {
self.attrs & (1 << (attr as u8)) != 0
}
pub fn remove(&mut self, attr: BoolAttr) {
self.attrs &= !(1 << attr as u8);
}
pub(super) fn iter(&self) -> impl Iterator<Item = BoolAttr> + '_ {
(0..(BoolAttr::COUNT as u8)).filter_map(|idx| {
if self.attrs & (1 << idx) != 0 {
Some(BoolAttr::from_repr(idx).unwrap())
} else {
None
}
})
}
}
#[cfg(feature = "serialize")]
impl serde::Serialize for InlineAttributes {
fn serialize<S>(&self, ser: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.to_owned().attrs.serialize(ser)
}
}