use crate::Primitive;
use calyx_utils::Id;
use itertools::Itertools;
use linked_hash_map::LinkedHashMap;
use std::path::PathBuf;
#[derive(Debug)]
pub enum PrimitiveInfo {
Extern {
path: PathBuf,
primitives: LinkedHashMap<Id, Primitive>,
is_source: bool,
},
Inline {
primitive: Primitive,
is_source: bool,
},
}
impl PrimitiveInfo {
pub fn ext(
path: PathBuf,
primitives: LinkedHashMap<Id, Primitive>,
) -> Self {
PrimitiveInfo::Extern {
path,
primitives,
is_source: false,
}
}
pub fn inline(primitive: Primitive) -> Self {
PrimitiveInfo::Inline {
primitive,
is_source: false,
}
}
pub fn is_source(&self) -> bool {
match self {
PrimitiveInfo::Extern { is_source, .. } => *is_source,
PrimitiveInfo::Inline { is_source, .. } => *is_source,
}
}
pub fn set_source(&mut self) {
match self {
PrimitiveInfo::Extern { is_source, .. } => *is_source = true,
PrimitiveInfo::Inline { is_source, .. } => *is_source = true,
}
}
}
#[derive(Debug, Default)]
pub struct LibrarySignatures {
prims: Vec<PrimitiveInfo>,
}
impl LibrarySignatures {
pub fn add_inline_primitive(
&mut self,
primitive: Primitive,
) -> &mut PrimitiveInfo {
assert!(
primitive.body.is_some(),
"inline primitive must have a body"
);
let name = primitive.name;
if self.find_primitive(name).is_some() {
panic!("Primitive `{}` is already defined in the context.", name);
}
let prim = PrimitiveInfo::inline(primitive);
self.prims.push(prim);
self.prims.last_mut().unwrap()
}
pub fn add_extern_primitive(
&mut self,
file: PathBuf,
primitive: Primitive,
) {
assert!(
primitive.body.is_none(),
"non-inline primitive must not have a body"
);
let name = primitive.name;
if self.find_primitive(name).is_some() {
panic!("Primitive `{}` is already defined in the context.", name);
}
let definined_ext = self.prims.iter_mut().find(|prim| match prim {
PrimitiveInfo::Extern { path, .. } => path == &file,
_ => false,
});
if let Some(PrimitiveInfo::Extern { primitives, .. }) = definined_ext {
primitives.insert(name, primitive);
} else {
let mut primitives = LinkedHashMap::new();
primitives.insert(name, primitive);
self.prims.push(PrimitiveInfo::ext(file, primitives));
}
}
pub(crate) fn add_extern(
&mut self,
file: PathBuf,
prims: Vec<Primitive>,
) -> &mut PrimitiveInfo {
let definined_ext = self.prims.iter().any(|prim| match prim {
PrimitiveInfo::Extern { path, .. } => path == &file,
_ => false,
});
if definined_ext {
panic!(
"Extern block with file `{}` is already defined in the context",
file.display()
);
}
let ext = PrimitiveInfo::ext(
file,
prims.into_iter().map(|p| (p.name, p)).collect(),
);
self.prims.push(ext);
self.prims.last_mut().unwrap()
}
pub fn find_primitive<S>(&self, name: S) -> Option<&Primitive>
where
S: Into<Id>,
{
let key = name.into();
self.prims.iter().find_map(|prim| match prim {
PrimitiveInfo::Extern { primitives, .. } => primitives.get(&key),
PrimitiveInfo::Inline { primitive, .. } => {
if primitive.name == key {
Some(primitive)
} else {
None
}
}
})
}
pub fn get_primitive<S>(&self, name: S) -> &Primitive
where
S: Into<Id>,
{
let key = name.into();
self.find_primitive(key).unwrap_or_else(|| {
panic!("Primitive `{}` is not defined in the context.", key)
})
}
pub fn mark_inline_source(&mut self, name: Id) {
let Some(inlined) = self.prims.iter_mut().find(|prim| match prim {
PrimitiveInfo::Inline { primitive, .. } => primitive.name == name,
PrimitiveInfo::Extern { .. } => false,
}) else {
panic!("Primitive `{}` is not defined in the context.", name);
};
inlined.set_source()
}
pub fn mark_extern_source(&mut self, path: PathBuf) {
let Some(ext_def) = self.prims.iter_mut().find(|prim| match prim {
PrimitiveInfo::Extern { path: p, .. } => p == &path,
PrimitiveInfo::Inline { .. } => false,
}) else {
panic!(
"extern file `{}` is not defined in the context",
path.to_string_lossy()
);
};
ext_def.set_source()
}
pub fn signatures(&self) -> impl Iterator<Item = &Primitive> + '_ {
self.prims.iter().flat_map(|prim| match prim {
PrimitiveInfo::Extern { primitives, .. } => {
primitives.values().collect_vec()
}
PrimitiveInfo::Inline { primitive, .. } => vec![primitive],
})
}
pub fn prim_infos(&self) -> &Vec<PrimitiveInfo> {
&self.prims
}
pub fn prim_inlines(
&self,
) -> impl Iterator<Item = (&Primitive, bool)> + '_ {
self.prims.iter().flat_map(|prim| match prim {
PrimitiveInfo::Extern { .. } => None,
PrimitiveInfo::Inline {
primitive,
is_source,
} => Some((primitive, *is_source)),
})
}
pub fn extern_paths(&self) -> Vec<&PathBuf> {
self.prims
.iter()
.filter_map(|p| match p {
PrimitiveInfo::Extern { path, .. } => Some(path),
PrimitiveInfo::Inline { .. } => None,
})
.collect_vec()
}
}