Skip to content

Commit

Permalink
add new iter and add some tests
Browse files Browse the repository at this point in the history
  • Loading branch information
vincenthz committed Feb 23, 2024
1 parent c4761f4 commit d953f4e
Show file tree
Hide file tree
Showing 5 changed files with 225 additions and 28 deletions.
6 changes: 3 additions & 3 deletions werbolg-compile/src/bindings/global_bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ impl GlobalBindings {
let (namespace, ident) = name.split();

if !self.0.namespace_exist(namespace.clone()) {
self.0.add_ns_hier(namespace.clone()).unwrap()
self.0.add_ns_empty_hier(namespace.clone()).unwrap()
}

self.0
Expand All @@ -27,13 +27,13 @@ impl GlobalBindings {
#[allow(unused)]
pub fn get(&self, name: &AbsPath) -> Option<&BindingType> {
let (namespace, ident) = name.split();
let bindings = self.0.get(&namespace).unwrap();
let bindings = self.0.get(&namespace).ok()?;
bindings.get(&ident)
}

#[allow(unused)]
pub fn iter<'a>(&'a self) -> impl Iterator<Item = (AbsPath, &'a BindingType)> {
self.0.iterator(
self.0.flat_iterator(
Namespace::root(),
alloc::rc::Rc::new(|x: &'a Bindings<BindingType>| alloc::boxed::Box::new(x.iter())),
)
Expand Down
233 changes: 214 additions & 19 deletions werbolg-compile/src/hier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,17 @@ impl<T: Default> Default for Hier<T> {
}
}

#[derive(Debug)]
pub struct HierError<E> {
pub namespace: Namespace,
pub err: Option<E>,
}

pub enum HierSearchError {

Check warning on line 24 in werbolg-compile/src/hier.rs

View workflow job for this annotation

GitHub Actions / Test Suite (ubuntu-latest)

enum `HierSearchError` is never used

Check warning on line 24 in werbolg-compile/src/hier.rs

View workflow job for this annotation

GitHub Actions / Check (ubuntu-latest)

enum `HierSearchError` is never used

Check warning on line 24 in werbolg-compile/src/hier.rs

View workflow job for this annotation

GitHub Actions / Check (macos-latest)

enum `HierSearchError` is never used

Check warning on line 24 in werbolg-compile/src/hier.rs

View workflow job for this annotation

GitHub Actions / Test Suite (macos-latest)

enum `HierSearchError` is never used

Check warning on line 24 in werbolg-compile/src/hier.rs

View workflow job for this annotation

GitHub Actions / Test Suite (windows-latest)

enum `HierSearchError` is never used

Check warning on line 24 in werbolg-compile/src/hier.rs

View workflow job for this annotation

GitHub Actions / Check (windows-latest)

enum `HierSearchError` is never used
ParentNotExisting(Namespace),
HierAlreadyExist { parent: Namespace, ident: Ident },
}

impl<T: Default> Hier<T> {
pub fn new(current: T) -> Self {
Self {
Expand Down Expand Up @@ -51,16 +57,39 @@ impl<T: Default> Hier<T> {
}
}

pub fn add_ns_hier(&mut self, namespace: Namespace) -> Result<(), ()> {
pub fn add_ns_hier(&mut self, namespace: Namespace, sub_hier: Hier<T>) -> Result<(), ()> {

Check warning on line 60 in werbolg-compile/src/hier.rs

View workflow job for this annotation

GitHub Actions / Test Suite (ubuntu-latest)

method `add_ns_hier` is never used

Check warning on line 60 in werbolg-compile/src/hier.rs

View workflow job for this annotation

GitHub Actions / Check (ubuntu-latest)

method `add_ns_hier` is never used

Check warning on line 60 in werbolg-compile/src/hier.rs

View workflow job for this annotation

GitHub Actions / Check (macos-latest)

method `add_ns_hier` is never used

Check warning on line 60 in werbolg-compile/src/hier.rs

View workflow job for this annotation

GitHub Actions / Test Suite (macos-latest)

method `add_ns_hier` is never used

Check warning on line 60 in werbolg-compile/src/hier.rs

View workflow job for this annotation

GitHub Actions / Test Suite (windows-latest)

method `add_ns_hier` is never used

Check warning on line 60 in werbolg-compile/src/hier.rs

View workflow job for this annotation

GitHub Actions / Check (windows-latest)

method `add_ns_hier` is never used
if namespace.is_root() {
return Err(());
}
let mut nss = &mut self.ns;
let mut namespace = namespace;
loop {
let (ns, sub_ns) = namespace.drop_first();
if sub_ns.is_root() {
let dup = nss.insert(ns, sub_hier).is_some();
break if dup { Err(()) } else { Ok(()) };
} else {
let sub = nss.get_mut(&ns);
if let Some(sub) = sub {
nss = &mut sub.ns;
namespace = sub_ns;
} else {
break Err(());
}
}
}
}

pub fn add_ns_empty_hier(&mut self, namespace: Namespace) -> Result<(), ()> {
if namespace.is_root() {
Ok(())
} else {
let (id, next) = namespace.clone().drop_first();
if let Some(r) = self.ns.get_mut(&id) {
r.add_ns_hier(next)
r.add_ns_empty_hier(next)
} else {
let mut h = Hier::new(T::default());
h.add_ns_hier(next)?;
h.add_ns_empty_hier(next)?;
self.ns.insert(id, h);
Ok(())
}
Expand Down Expand Up @@ -133,39 +162,100 @@ impl<T: Default> Hier<T> {
}

impl<T> Hier<T> {
pub fn iterator<'a, I, J, F>(
pub fn flat_iterator<'a, I, J, F>(
&'a self,
namespace: Namespace,
f: alloc::rc::Rc<F>,
) -> HierIterator<'a, I, J, T, F>
) -> HierFlatIterator<'a, I, J, T, F>
where
F: Fn(&'a T) -> J,
J: Iterator<Item = (&'a Ident, &'a I)>,
{
HierIterator::<'a> {
HierFlatIterator::<'a> {
namespace,
current_consumed: false,
current: CurrentOrSub::Current(f(&self.current)),
ns: self.ns.iter(),
f: f,
}
}

pub fn hier_iterator<'a, I, F>(
&'a self,
namespace: Namespace,
f: alloc::rc::Rc<F>,
) -> HierIterator<'a, I, T, F>
where
F: Fn(&'a T) -> I,
{
HierIterator {
namespace,
current: Some(f(&self.current)),
hier_iter: None,
ns: self.ns.iter(),
f: f,
}
}
}

pub struct HierIterator<'a, I: 'a, T, F> {
namespace: Namespace,
current: Option<I>,
hier_iter: Option<Box<HierIterator<'a, I, T, F>>>,
ns: hash_map::Iter<'a, Ident, Hier<T>>,
f: alloc::rc::Rc<F>,
}

impl<'a, I: 'a, T, F> Iterator for HierIterator<'a, I, T, F>
where
F: Fn(&'a T) -> I,
{
type Item = (Namespace, I);

fn next(&mut self) -> Option<Self::Item> {
let mut current = None;
core::mem::swap(&mut self.current, &mut current);

if let Some(current) = current {
Some((self.namespace.clone(), current))
} else {
loop {
let mut hier_iter_var = None;
core::mem::swap(&mut self.hier_iter, &mut hier_iter_var);

if let Some(mut hier_iter) = hier_iter_var {
let x = hier_iter.next();
if x.is_some() {
self.hier_iter = Some(hier_iter);
break x;
}
// if none, then we iterate
} else {
if let Some((next_ident, next_hier)) = self.ns.next() {
let next_namespace = self.namespace.clone().append(next_ident.clone());
let x = next_hier.hier_iterator(next_namespace, self.f.clone());
self.hier_iter = Some(Box::new(x));
} else {
break None;
}
}
}
}
}
}

/// Hierarchy iterator for Hier<T>
/// Flat hierarchy iterator for Hier<T>
///
/// 'a is the lifetime of the initial Hier<T>
/// I is the item inside the second element of the Iterator of the T
/// J is the iterator object created from T which return a Tuple of elements, (Ident, I)
/// T is the object embedded inside Hier
/// F is the closure to create the iterator associated with T
pub struct HierIterator<'a, I: 'a, J, T, F: Fn(&'a T) -> J>
pub struct HierFlatIterator<'a, I: 'a, J, T, F: Fn(&'a T) -> J>
where
J: Iterator<Item = (&'a Ident, &'a I)>,
{
namespace: Namespace,
current_consumed: bool,
current: CurrentOrSub<J, Box<HierIterator<'a, I, J, T, F>>>,
current: CurrentOrSub<J, Box<HierFlatIterator<'a, I, J, T, F>>>,
ns: hash_map::Iter<'a, Ident, Hier<T>>,
f: alloc::rc::Rc<F>,
}
Expand All @@ -175,7 +265,7 @@ pub enum CurrentOrSub<A, B> {
Sub(B),
}

impl<'a, I, J, T, F: Fn(&'a T) -> J> Iterator for HierIterator<'a, I, J, T, F>
impl<'a, I, J, T, F: Fn(&'a T) -> J> Iterator for HierFlatIterator<'a, I, J, T, F>
where
J: Iterator<Item = (&'a Ident, &'a I)>,
{
Expand All @@ -196,20 +286,125 @@ where
if let Some(x) = next {
Some(x)
} else {
if self.current_consumed {
self.namespace = self.namespace.parent();
} else {
self.current_consumed = true;
}
match self.ns.next() {
None => None,
Some((ns, hier)) => {
self.namespace = self.namespace.clone().append(ns.clone());
let x = hier.iterator(self.namespace.clone(), self.f.clone());
let sub_namespace = self.namespace.clone().append(ns.clone());
let x = hier.flat_iterator(sub_namespace, self.f.clone());
self.current = CurrentOrSub::Sub(Box::new(x));
self.next()
}
}
}
}
}

#[cfg(test)]
mod tests {
use super::*;
use alloc::rc::Rc;
use alloc::vec;

type FakeMap = HashMap<Ident, u32>;

fn get_iter<'a>(map: &'a FakeMap) -> hashbrown::hash_map::Iter<'a, Ident, u32> {
map.iter()
}

fn fake_hier() -> Hier<FakeMap> {
// create the following hier
// [ ("a", 10), ("sub::b"), 11), ("sub2::subsub::c", 12) ]

let mut root_h: FakeMap = HashMap::new();
root_h.insert(Ident::from("a"), 10u32);

let mut sub_h: FakeMap = HashMap::new();
sub_h.insert(Ident::from("b"), 11u32);

let mut sub_sub_h: FakeMap = HashMap::new();
sub_sub_h.insert(Ident::from("c"), 12u32);

let mut hier = Hier::new(root_h);
let mut sub_hier = Hier::new(FakeMap::new());
sub_hier
.add_ns_hier(
Namespace::root().append(Ident::from("subsub")),
Hier::new(sub_sub_h),
)
.unwrap();

hier.add_ns(Ident::from("sub"), sub_h).unwrap();
hier.add_ns_hier(Namespace::root().append(Ident::from("sub2")), sub_hier)
.unwrap();

hier
}

#[test]
fn works_hier() {
let hier = fake_hier();
let vals = hier
.hier_iterator(Namespace::root(), Rc::new(get_iter))
.collect::<Vec<_>>();

let expected_namespaces = [
Namespace::root(),
Namespace::root().append(Ident::from("sub")),
Namespace::root().append(Ident::from("sub2")),
Namespace::root()
.append(Ident::from("sub2"))
.append(Ident::from("subsub")),
];
let expected_values = [
vec![(Ident::from("a"), &10u32)],
vec![(Ident::from("b"), &11u32)],
vec![],
vec![(Ident::from("c"), &12u32)],
];

let mut bits = 0b1111;
for (namespace, i) in vals.into_iter() {
let x = i.map(|(i, x)| (i.clone(), x)).collect::<Vec<_>>();

let mut found = false;
for (i, (n, vs)) in expected_namespaces
.iter()
.zip(expected_values.iter())
.enumerate()
{
if &namespace == n {
if bits & (1 << i) == 0 {
panic!("duplicated call to namespace {:?}", n);
}
assert_eq!(vs, &x, "not equal in namespace {:?}", n);
bits = bits & !(1 << i);
found = true;
break;
}
}
if !found {
panic!("unexpected namespace {:?}", namespace)
}
}
assert_eq!(bits, 0)
}

#[test]
fn works_flat() {
let hier = fake_hier();

let mut vals = hier
.flat_iterator(Namespace::root(), Rc::new(get_iter))
.collect::<Vec<_>>();
vals.sort_by(|(a, _), (b, _)| a.cmp(b));
let sub = Namespace::root().append(Ident::from("sub"));
let sub2 = Namespace::root()
.append(Ident::from("sub2"))
.append(Ident::from("subsub"));
let a_path = AbsPath::new(&Namespace::root(), &Ident::from("a"));
let b_path = AbsPath::new(&sub, &Ident::from("b"));
let c_path = AbsPath::new(&sub2, &Ident::from("c"));

assert_eq!(vals, vec![(a_path, &10), (b_path, &11), (c_path, &12)]);
}
}
6 changes: 4 additions & 2 deletions werbolg-compile/src/prepare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,15 @@ impl<L: Clone + Eq + core::hash::Hash> CompilationState<L> {
) -> Result<CompilationUnit<L>, CompilationError> {
let SymbolsTableData { table, vecdata } = self.funs;

/*
for (p, _id) in table.to_vec(Namespace::root()) {
std::println!("{:?}", p)
}
*/

let mut root_bindings = GlobalBindings::new();

for (path, id) in environ.symbols.to_vec(Namespace::root()) {
for (path, id) in environ.symbols.iter() {
// unwrap is ok here, the environment should check for duplicate symbol and
// missing namespace
match id {
Expand All @@ -115,7 +117,7 @@ impl<L: Clone + Eq + core::hash::Hash> CompilationState<L> {
}
}

for (path, fun_id) in table.to_vec(Namespace::root()) {
for (path, fun_id) in table.iter() {
root_bindings
.add(path.clone(), BindingType::Fun(fun_id))
.map_err(|()| {
Expand Down
4 changes: 2 additions & 2 deletions werbolg-compile/src/symbols.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ impl<ID: Copy> SymbolsTable<ID> {

pub fn create_namespace(&mut self, namespace: Namespace) -> Result<(), NamespaceError> {
self.0
.add_ns_hier(namespace.clone())
.add_ns_empty_hier(namespace.clone())
.map_err(|()| NamespaceError::DuplicateLeaf(namespace))
}

Expand Down Expand Up @@ -150,7 +150,7 @@ impl<ID: Copy> SymbolsTable<ID> {

pub fn iter<'a>(&'a self) -> impl Iterator<Item = (AbsPath, ID)> + 'a {
self.0
.iterator(
.flat_iterator(
Namespace::root(),
alloc::rc::Rc::new(|s: &'a SymbolsTableFlat<ID>| s.iter()),
)
Expand Down
4 changes: 2 additions & 2 deletions werbolg-core/src/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use core::num::NonZeroUsize;
///
/// Note that the ident can contains pretty much anything the frontend wants.
/// For example, Space or '::' could be inside the ident
#[derive(Clone, PartialEq, Eq, Hash)]
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Ident(pub String);

impl core::fmt::Debug for Ident {
Expand Down Expand Up @@ -183,7 +183,7 @@ impl<'a> Iterator for PathDecomposer<'a> {
/// An absolute Path (e.g. namespace::function, or a.g.d)
///
/// The path cannot be empty
#[derive(Clone, PartialEq, Eq, Hash)]
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct AbsPath(Vec<Ident>);

impl core::fmt::Debug for AbsPath {
Expand Down

0 comments on commit d953f4e

Please sign in to comment.