use crate::ast::*;
use crate::data::*;
use crate::named_entity::*;
use crate::data::error_codes::ErrorCode;
use crate::TokenSpan;
use fnv::FnvHashMap;
use std::cell::RefCell;
use std::collections::hash_map::Entry;
use std::rc::Rc;
use vhdl_lang::TokenAccess;
#[derive(Default, Clone)]
pub(crate) struct Scope<'a>(Rc<RefCell<ScopeInner<'a>>>);
#[derive(Default)]
struct ScopeInner<'a> {
parent: Option<Scope<'a>>,
region: Region<'a>,
cache: FnvHashMap<Designator, NamedEntities<'a>>,
anon_idx: usize,
}
#[derive(Debug)]
pub(crate) enum UndeclaredKind {
Identifier(Symbol),
Operator(Operator),
Character(u8),
Anonymous,
}
#[derive(Debug)]
pub(crate) enum LookupError {
IntoUnambiguousError(IntoUnambiguousError),
Undeclared(UndeclaredKind),
}
impl From<IntoUnambiguousError> for LookupError {
fn from(value: IntoUnambiguousError) -> Self {
Self::IntoUnambiguousError(value)
}
}
impl LookupError {
pub fn into_diagnostic(self, ctx: &dyn TokenAccess, span: impl Into<TokenSpan>) -> Diagnostic {
let span = span.into();
match self {
LookupError::IntoUnambiguousError(err) => err.into_diagnostic(ctx, span),
LookupError::Undeclared(kind) => {
let msg = match kind {
UndeclaredKind::Identifier(ident) => format!("No declaration of '{ident}'"),
UndeclaredKind::Operator(operator) => {
format!("No declaration of operator '{operator}'")
}
UndeclaredKind::Character(chr) => format!("No declaration of '{chr}'"),
UndeclaredKind::Anonymous => "No declaration of <anonymous>".to_owned(),
};
Diagnostic::new(span.pos(ctx), msg, ErrorCode::Unresolved)
}
}
}
}
impl<'a> ScopeInner<'a> {
pub fn into_region(self) -> Region<'a> {
self.region
}
pub fn into_visibility(self) -> Visibility<'a> {
self.region.visibility
}
pub fn close(&self, diagnostics: &mut dyn DiagnosticHandler) {
self.region.close(diagnostics)
}
pub fn add(&mut self, ent: EntRef<'a>, diagnostics: &mut dyn DiagnosticHandler) {
self.cache.remove(&ent.designator);
self.region.add(ent, diagnostics)
}
fn make_potentially_visible(
&mut self,
visible_pos: Option<&SrcPos>,
designator: Designator,
ent: EntRef<'a>,
) {
self.cache.remove(&ent.designator);
self.region
.visibility
.make_potentially_visible_with_name(visible_pos, designator, ent);
}
pub fn make_all_potentially_visible(
&mut self,
visible_pos: Option<&SrcPos>,
region: &'a Region<'a>,
) {
self.cache.clear();
self.region
.visibility
.make_all_potentially_visible(visible_pos, region);
}
pub fn add_context_visibility(&mut self, visible_pos: Option<&SrcPos>, region: &Region<'a>) {
self.cache.clear();
self.region
.visibility
.add_context_visibility(visible_pos, ®ion.visibility);
}
pub fn lookup_immediate(&self, designator: &Designator) -> Option<&NamedEntities<'a>> {
self.region.lookup_immediate(designator)
}
fn lookup_enclosing(&self, designator: &Designator) -> Option<NamedEntities<'a>> {
match self.lookup_immediate(designator).cloned() {
Some(NamedEntities::Single(single)) => Some(NamedEntities::Single(single)),
Some(NamedEntities::Overloaded(immediate)) => {
if let Some(NamedEntities::Overloaded(enclosing)) = self
.parent
.as_ref()
.and_then(|region| region.0.borrow().lookup_enclosing(designator))
{
Some(NamedEntities::Overloaded(immediate.with_visible(enclosing)))
} else {
Some(NamedEntities::Overloaded(immediate))
}
}
None => self
.parent
.as_ref()
.and_then(|region| region.0.borrow().lookup_enclosing(designator)),
}
}
fn lookup_visiblity_into(&self, designator: &Designator, visible: &mut Visible<'a>) {
self.region.visibility.lookup_into(designator, visible);
if let Some(ref parent) = self.parent {
parent.0.borrow().lookup_visiblity_into(designator, visible);
}
}
fn lookup_visible(
&self,
designator: &Designator,
) -> Result<Option<NamedEntities<'a>>, LookupError> {
let mut visible = Visible::default();
self.lookup_visiblity_into(designator, &mut visible);
visible
.into_unambiguous(designator)
.map_err(|err| err.into())
}
fn lookup_uncached(&self, designator: &Designator) -> Result<NamedEntities<'a>, LookupError> {
let result = if let Some(enclosing) = self.lookup_enclosing(designator) {
match enclosing {
NamedEntities::Single(..) => Some(enclosing),
NamedEntities::Overloaded(enclosing_overloaded) => {
if let Ok(Some(NamedEntities::Overloaded(overloaded))) =
self.lookup_visible(designator)
{
Some(NamedEntities::Overloaded(
enclosing_overloaded.with_visible(overloaded),
))
} else {
Some(NamedEntities::Overloaded(enclosing_overloaded))
}
}
}
} else {
self.lookup_visible(designator)?
};
match result {
Some(visible) => Ok(visible),
None => Err(LookupError::Undeclared(match designator {
Designator::Identifier(ident) => UndeclaredKind::Identifier(ident.clone()),
Designator::OperatorSymbol(operator) => UndeclaredKind::Operator(*operator),
Designator::Character(chr) => UndeclaredKind::Character(*chr),
Designator::Anonymous(_) => UndeclaredKind::Anonymous,
})),
}
}
fn lookup(&mut self, designator: &Designator) -> Result<NamedEntities<'a>, LookupError> {
if let Some(res) = self.cache.get(designator) {
return Ok(res.clone());
}
let ents = self.lookup_uncached(designator)?;
if let Entry::Vacant(vacant) = self.cache.entry(designator.clone()) {
Ok(vacant.insert(ents).clone())
} else {
unreachable!("Cache miss cannot be followed by occupied entry")
}
}
}
impl<'a> Scope<'a> {
pub fn new(region: Region<'a>) -> Scope<'a> {
Self(Rc::new(RefCell::new(ScopeInner {
parent: None,
region,
cache: Default::default(),
anon_idx: 0,
})))
}
pub fn nested(&self) -> Scope<'a> {
Self(Rc::new(RefCell::new(ScopeInner {
region: Region::default(),
parent: Some(self.clone()),
cache: self.0.borrow().cache.clone(),
anon_idx: 0,
})))
}
pub fn with_parent(self, scope: &Scope<'a>) -> Scope<'a> {
Self(Rc::new(RefCell::new(ScopeInner {
parent: Some(scope.clone()),
region: self.into_inner().region,
cache: Default::default(),
anon_idx: 0,
})))
}
pub fn extend(region: &Region<'a>, parent: Option<&Scope<'a>>) -> Scope<'a> {
let kind = match region.kind {
RegionKind::PackageDeclaration => RegionKind::PackageBody,
_ => RegionKind::Other,
};
let extended_region = Region {
visibility: region.visibility.clone(),
entities: region.entities.clone(),
kind,
};
if let Some(parent) = parent {
Scope::new(extended_region).with_parent(parent)
} else {
Scope::new(extended_region)
}
}
pub fn in_package_declaration(self) -> Scope<'a> {
let inner = self.into_inner();
Self(Rc::new(RefCell::new(ScopeInner {
parent: inner.parent,
region: inner.region.in_package_declaration(),
cache: inner.cache,
anon_idx: inner.anon_idx,
})))
}
pub fn add(&self, ent: EntRef<'a>, diagnostics: &mut dyn DiagnosticHandler) {
self.0.as_ref().borrow_mut().add(ent, diagnostics);
}
pub fn make_potentially_visible(&self, visible_pos: Option<&SrcPos>, ent: EntRef<'a>) {
self.0.as_ref().borrow_mut().make_potentially_visible(
visible_pos,
ent.designator().clone(),
ent,
);
}
pub fn make_potentially_visible_with_name(
&self,
visible_pos: Option<&SrcPos>,
designator: Designator,
ent: EntRef<'a>,
) {
self.0
.as_ref()
.borrow_mut()
.make_potentially_visible(visible_pos, designator, ent);
}
pub fn make_all_potentially_visible(
&self,
visible_pos: Option<&SrcPos>,
region: &'a Region<'a>,
) {
self.0
.as_ref()
.borrow_mut()
.make_all_potentially_visible(visible_pos, region);
}
pub fn close(&self, diagnostics: &mut dyn DiagnosticHandler) {
self.0.as_ref().borrow().close(diagnostics)
}
fn into_inner(self) -> ScopeInner<'a> {
if let Ok(cell) = Rc::try_unwrap(self.0) {
cell.into_inner()
} else {
panic!("Expect no child regions");
}
}
pub fn into_region(self) -> Region<'a> {
self.into_inner().into_region()
}
pub fn into_visibility(self) -> Visibility<'a> {
self.into_inner().into_visibility()
}
pub fn lookup_immediate(&self, designator: &Designator) -> Option<NamedEntities<'a>> {
let inner = self.0.as_ref().borrow();
let names = inner.lookup_immediate(designator)?;
Some(names.clone())
}
pub fn lookup(&self, designator: &Designator) -> Result<NamedEntities<'a>, LookupError> {
self.0.as_ref().borrow_mut().lookup(designator)
}
pub fn add_context_visibility(&self, visible_pos: Option<&SrcPos>, region: &Region<'a>) {
self.0
.as_ref()
.borrow_mut()
.add_context_visibility(visible_pos, region)
}
pub fn next_anonymous(&self) -> usize {
let mut inner = self.0.borrow_mut();
let idx = inner.anon_idx;
inner.anon_idx += 1;
idx
}
pub fn anonymous_designator(&self) -> Designator {
Designator::Anonymous(self.next_anonymous())
}
}
impl<'a> NamedEntities<'a> {
pub(crate) fn make_potentially_visible_in(
&self,
visible_pos: Option<&SrcPos>,
scope: &Scope<'a>,
) {
match self {
Self::Single(ent) => {
scope.make_potentially_visible(visible_pos, ent);
}
Self::Overloaded(overloaded) => {
for ent in overloaded.entities() {
scope.make_potentially_visible(visible_pos, ent.into());
}
}
}
}
}
impl Diagnostic {
pub(crate) fn duplicate_error(
name: &impl std::fmt::Display,
pos: &SrcPos,
prev_pos: Option<&SrcPos>,
) -> Diagnostic {
let mut diagnostic = Diagnostic::new(
pos,
format!("Duplicate declaration of '{name}'"),
ErrorCode::Duplicate,
);
if let Some(prev_pos) = prev_pos {
diagnostic.add_related(prev_pos, "Previously defined here");
}
diagnostic
}
}