use crate::*;
use perfect_derive::perfect_derive;
use std::{hash::Hash, sync::Arc};
use itertools::Itertools;
pub struct LookupSet<'s, 'i, T: Hash + Eq, V = &'s T> {
name: Identifier<'i>,
scope: &'s Scope<T>,
iterator: Box<dyn 's + Iterator<Item = &'s T>>,
filter_map: Box<dyn 's + Fn(&'s T) -> Result<Option<V>>>,
empty: bool,
}
impl<'s, 'i, T: 's + Hash + Eq, V: 's> LookupSet<'s, 'i, T, V> {
fn new(
name: Identifier<'i>,
scope: &'s Scope<T>,
entities: impl 's + IntoIterator<Item = &'s T>,
filter_map: impl 's + Fn(&'s T) -> Result<Option<V>>,
) -> Self {
let mut iterator = entities.into_iter().peekable();
let empty = iterator.peek().is_none();
LookupSet {
name,
scope,
iterator: Box::new(iterator),
filter_map: Box::new(filter_map),
empty,
}
}
pub fn name(&self) -> &Identifier<'_> {
&self.name
}
pub fn is_empty(&self) -> bool {
self.empty
}
pub fn filter_map_ok<R: 's>(
self,
f: impl 's + Fn(V) -> Result<Option<R>>,
) -> LookupSet<'s, 'i, T, R> {
let filter_map = self.filter_map;
LookupSet::new(
self.name,
self.scope,
self.iterator,
move |t| match filter_map(t) {
Ok(Some(v)) => f(v),
Ok(None) => Ok(None),
Err(err) => Err(err),
},
)
}
pub fn filter_map<R: 's>(self, f: impl 's + Fn(V) -> Option<R>) -> LookupSet<'s, 'i, T, R> {
self.filter_map_ok(move |v| Ok(f(v)))
}
pub fn filter(self, f: impl 's + Fn(&V) -> bool) -> Self {
self.filter_map(move |v| if f(&v) { Some(v) } else { None })
}
pub fn filter_ok(self, f: impl 's + Fn(&V) -> Result<bool>) -> Self {
self.filter_map_ok(move |v| if f(&v)? { Ok(Some(v)) } else { Ok(None) })
}
pub fn map<R: 's>(self, f: impl 's + Fn(V) -> R) -> LookupSet<'s, 'i, T, R> {
self.filter_map(move |v| Some(f(v)))
}
pub fn all(self) -> Result<Vec<V>> {
self.iterator
.map(move |t| (self.filter_map)(t))
.process_results(|c| c.flatten().collect())
}
pub fn or_else(self, other: impl 's + FnOnce() -> LookupSet<'s, 'i, T, V>) -> Self {
let mut this = self.iterator.peekable();
if this.peek().is_some() {
LookupSet::new(self.name, self.scope, this, self.filter_map)
} else {
other()
}
}
}
impl<'s, 'i, T: 's + Clone + Hash + Eq + Located, V: 's> LookupSet<'s, 'i, T, V> {
pub fn one(self) -> Result<V> {
let mut all = Vec::new();
let mut filtered = Vec::new();
for t in self.iterator {
if let Some(v) = (self.filter_map)(t)? {
filtered.push((t, v));
}
all.push(t);
}
let mut filtered = filtered.into_iter();
let all = all.into_iter();
match (filtered.next(), filtered.next()) {
(Some((_, v)), None) => Ok(v),
(Some((t1, _)), Some((t2, _))) => {
error!(
self.name.span(),
"ambiguous reference to symbol '{}'", self.name
);
note!(t1.span(), "matching symbol declared here");
note!(t2.span(), "matching symbol declared here");
for (t, _) in filtered {
note!(t.span(), "matching symbol declared here");
}
Err(DiagnosticEmitted)
}
(None, _) => {
let mut all = all.peekable();
match all.peek() {
Some(_) => {
error!(self.name.span(), "no matching symbol '{}'", self.name);
for element in all {
if element.span().is_some() {
note!(element.span(), "non-matching symbol declared here");
}
}
Err(DiagnosticEmitted)
}
None => {
error!(
self.name.span(),
"undefined reference to symbol '{}'", self.name
);
Err(DiagnosticEmitted)
}
}
}
}
}
}
#[perfect_derive(Default, Clone)]
pub struct Scope<T: Hash + Eq> {
elements: Stacked<rpds::HashTrieMapSync<String, rpds::HashTrieSetSync<T>>>,
parent: Option<Arc<Scope<T>>>,
}
impl<T: Hash + Eq> Scope<T> {
pub fn new() -> Self {
Scope::default()
}
pub fn with_parent(self, parent: Scope<T>) -> Scope<T> {
Scope {
parent: Some(Arc::new(parent)),
..self
}
}
pub fn set_parent(&mut self, parent: Scope<T>) {
self.parent = Some(Arc::new(parent));
}
pub fn add(&mut self, name: &str, element: T) {
if !self.elements.contains_key(name) {
self.elements
.insert_mut(name.to_string(), rpds::HashTrieSetSync::new_sync());
}
self.elements.get_mut(name).unwrap().insert_mut(element);
}
pub fn lookup<'i>(&self, name: impl Into<Identifier<'i>>) -> LookupSet<'_, 'i, T, &'_ T> {
let name = name.into();
if let Some(elements) = self.elements.get(name.name()) {
LookupSet::new(name, self, elements.iter(), |t| Ok(Some(t)))
} else if let Some(parent) = &self.parent {
parent.lookup(name)
} else {
LookupSet::new(name, self, std::iter::empty(), |t| Ok(Some(t)))
}
}
}
impl<T: Clone + Hash + Eq> Scope<T> {
pub fn merge(&mut self, other: &Scope<T>) {
for (name, vec) in &*other.elements {
for element in vec {
self.add(name.as_str(), element.clone());
}
}
}
}
impl<T: Hash + Eq> Stack for Scope<T> {
fn push(&mut self) -> Result<()> {
self.elements.push()
}
fn pop_n(&mut self, n: usize) -> Result<()> {
self.elements.pop_n(n)
}
}