use crate::cat;
use crate::traits::Identify;
use crate::{NonNegative, Reversed};
#[derive(Debug, Clone, PartialEq)]
pub struct Array {
identifier: String,
pub size: usize,
pub typ: Box<Type>,
}
impl Identify for Array {
fn identifier(&self) -> &str {
self.identifier.as_str()
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Field {
name: String,
typ: Type,
reversed: bool,
}
impl Identify for Field {
fn identifier(&self) -> &str {
self.name.as_str()
}
}
impl Field {
pub fn new(name: impl Into<String>, typ: Type, reversed: bool) -> Field {
Field {
name: name.into(),
typ,
reversed,
}
}
pub fn typ(&self) -> &Type {
&self.typ
}
pub fn is_reversed(&self) -> bool {
self.reversed
}
}
impl Reversed for Field {
fn reversed(&self) -> Self {
Field::new(self.name.clone(), self.typ.clone(), !self.reversed)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Record {
identifier: String,
fields: Vec<Field>,
}
impl Identify for Record {
fn identifier(&self) -> &str {
self.identifier.as_str()
}
}
impl Record {
pub fn new(name: impl Into<String>, fields: Vec<Field>) -> Record {
Record {
identifier: name.into(),
fields,
}
}
pub fn new_empty(name: impl Into<String>) -> Record {
Record {
identifier: name.into(),
fields: vec![],
}
}
pub fn new_empty_stream(name: impl Into<String>) -> Record {
Record {
identifier: name.into(),
fields: vec![
Field::new("valid", Type::Bit, false),
Field::new("ready", Type::Bit, true),
],
}
}
pub fn insert_new_field(&mut self, name: impl Into<String>, typ: Type, reversed: bool) {
self.fields.push(Field::new(name, typ, reversed));
}
pub fn insert(&mut self, field: Field) {
self.fields.push(field);
}
pub fn has_reversed(&self) -> bool {
self.fields.iter().any(|i| i.reversed)
}
pub fn fields(&self) -> impl Iterator<Item = &Field> {
self.fields.iter()
}
pub fn is_empty(&self) -> bool {
self.fields.is_empty()
}
pub fn append_name_nested(&self, with: impl Into<String>) -> Self {
let p: String = with.into();
let mut result = Record::new_empty(cat!(self.identifier, p.clone()));
for f in self.fields() {
result.insert(match f.typ() {
Type::Record(r) => Field::new(
f.identifier(),
Type::Record(r.append_name_nested(p.clone())),
f.reversed,
),
_ => f.clone(),
});
}
result
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Type {
Bit,
BitVec {
width: NonNegative,
},
Array(Array),
Record(Record),
}
pub type TypeBundle = Vec<(Vec<String>, Type, bool)>;
impl Type {
pub fn bitvec(width: NonNegative) -> Type {
Type::BitVec { width }
}
pub fn record(name: impl Into<String>, fields: Vec<Field>) -> Type {
Type::Record(Record::new(name.into(), fields))
}
pub fn flatten(&self, prefix: Vec<String>, reversed: bool) -> TypeBundle {
let mut result: TypeBundle = vec![];
match self {
Type::Record(rec) => rec.fields.iter().for_each(|field| {
let mut new_prefix = prefix.clone();
new_prefix.push(field.name.clone());
result.extend(field.typ.flatten(new_prefix, field.reversed))
}),
_ => result.push((prefix, self.clone(), reversed)),
}
result
}
pub fn has_reversed(&self) -> bool {
match self {
Type::Record(rec) => rec.has_reversed(),
_ => false,
}
}
}
#[derive(Debug, Clone)]
pub struct Parameter {
pub name: String,
pub typ: Type,
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Mode {
In,
Out,
}
impl Reversed for Mode {
fn reversed(&self) -> Self {
match self {
Mode::In => Mode::Out,
Mode::Out => Mode::In,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Port {
identifier: String,
mode: Mode,
typ: Type,
}
impl Port {
pub fn new(name: impl Into<String>, mode: Mode, typ: Type) -> Port {
Port {
identifier: name.into(),
mode,
typ,
}
}
pub fn mode(&self) -> Mode {
self.mode
}
pub fn typ(&self) -> Type {
self.typ.clone()
}
}
impl Identify for Port {
fn identifier(&self) -> &str {
self.identifier.as_str()
}
}
#[derive(Debug, Clone)]
pub struct Component {
identifier: String,
parameters: Vec<Parameter>,
ports: Vec<Port>,
}
impl Identify for Component {
fn identifier(&self) -> &str {
self.identifier.as_str()
}
}
impl Component {
pub fn new(
identifier: impl Into<String>,
parameters: Vec<Parameter>,
ports: Vec<Port>,
) -> Component {
Component {
identifier: identifier.into(),
parameters,
ports,
}
}
pub fn ports(&self) -> &Vec<Port> {
&self.ports
}
pub fn parameters(&self) -> &Vec<Parameter> {
&self.parameters
}
pub fn flatten_types(&mut self) {
let mut new_ports: Vec<Port> = Vec::new();
self.ports.iter().for_each(|port| {
let bundle = port
.typ
.flatten(vec![port.identifier.clone()], port.mode == Mode::Out);
for tup in bundle {
new_ports.push(Port::new(
tup.0.join("_"),
if tup.2 { Mode::Out } else { Mode::In },
tup.1,
));
}
});
self.ports = new_ports;
}
}
#[derive(Debug)]
pub struct Library {
pub identifier: String,
pub components: Vec<Component>,
}
#[derive(Debug)]
pub struct Project {
pub identifier: String,
pub libraries: Vec<Library>,
}
#[cfg(test)]
pub(crate) mod test {
use super::*;
use crate::cat;
pub(crate) mod records {
use super::*;
pub(crate) fn prim(bits: u32) -> Type {
Type::bitvec(bits)
}
pub(crate) fn rec(name: impl Into<String>) -> Type {
Type::record(
cat!(name.into(), "type"),
vec![
Field::new("a", Type::bitvec(42), false),
Field::new("b", Type::bitvec(1337), false),
],
)
}
pub(crate) fn rec_of_single(name: impl Into<String>) -> Type {
Type::record(
cat!(name.into(), "type"),
vec![Field::new("a", Type::bitvec(42), false)],
)
}
pub(crate) fn rec_nested(name: impl Into<String>) -> Type {
let n: String = name.into();
Type::record(
cat!(n, "type"),
vec![
Field::new("c", rec(cat!(n, "c")), false),
Field::new("d", rec(cat!(n, "d")), false),
],
)
}
}
pub fn test_rec() -> Type {
Type::record(
"rec",
vec![
Field::new("a", Type::Bit, false),
Field::new("b", Type::bitvec(4), true),
],
)
}
pub fn test_rec_nested() -> Type {
Type::record(
cat!("rec", "nested"),
vec![
Field::new("a", Type::Bit, false),
Field::new("b", test_rec(), false),
],
)
}
pub fn test_comp() -> Component {
Component {
identifier: "test_comp".to_string(),
parameters: vec![],
ports: vec![
Port::new("a", Mode::In, test_rec()),
Port::new("b", Mode::Out, test_rec_nested()),
],
}
}
pub fn test_lib() -> Library {
Library {
identifier: "lib".to_string(),
components: vec![test_comp()],
}
}
pub fn test_proj() -> Project {
Project {
identifier: "proj".to_string(),
libraries: vec![test_lib()],
}
}
#[test]
fn test_flatten_rec() {
let flat = test_rec().flatten(vec![], false);
assert_eq!(flat[0].0, vec!["a".to_string()]);
assert_eq!(flat[0].1, Type::Bit);
assert_eq!(flat[0].2, false);
assert_eq!(flat[1].0, vec!["b".to_string()]);
assert_eq!(flat[1].1, Type::bitvec(4));
assert_eq!(flat[1].2, true);
}
#[test]
fn test_flatten_rec_nested() {
let flat = test_rec_nested().flatten(vec![], false);
assert_eq!(flat[0].0[0], "a".to_string());
assert_eq!(flat[0].1, Type::Bit);
assert_eq!(flat[0].2, false);
assert_eq!(flat[1].0[0], "b".to_string());
assert_eq!(flat[1].0[1], "a".to_string());
assert_eq!(flat[1].1, Type::Bit);
assert_eq!(flat[1].2, false);
assert_eq!(flat[2].0[0], "b".to_string());
assert_eq!(flat[2].0[1], "b".to_string());
assert_eq!(flat[2].1, Type::bitvec(4));
assert_eq!(flat[2].2, true);
}
#[test]
fn rec_has_reversed() {
assert!(test_rec().has_reversed());
assert!(!Type::record(
"test",
vec![
Field::new("a", Type::Bit, false),
Field::new("b", Type::Bit, false),
],
)
.has_reversed());
}
}