// Copyright (c) 2016-2021 Fabian Schuiki //! Facilities to manage declarations and resolve names. #![deny(missing_docs)] use std::collections::{HashMap, HashSet}; use crate::common::errors::*; use crate::common::score::Result; use crate::common::source::Spanned; use crate::common::Verbosity; use crate::score::{Def, ResolvableName, ScopeRef, ScoreContext}; /// A scope. #[derive(Clone, Debug)] pub struct Scope { /// The parent scope. pub parent: Option, /// The definitions made in this scope. pub defs: HashMap>>, /// The definitions imported from other scopes. pub imported_defs: HashMap>>, /// The explicitly imported scopes. pub imported_scopes: HashSet, } impl Scope { /// Create a new empty scope. pub fn new(parent: Option) -> Scope { Scope { parent: parent, defs: HashMap::new(), imported_defs: HashMap::new(), imported_scopes: HashSet::new(), } } } impl<'lazy, 'sb, 'ast, 'ctx> ScoreContext<'lazy, 'sb, 'ast, 'ctx> { /// Lookup a scope and perform an operation on it. pub fn with_scope(&self, scope: ScopeRef, f: F) -> Result where F: FnOnce(&mut Scope) -> Result, { let mut tbl = self.sb.scope2_table.borrow_mut(); let scp = match tbl.get_mut(&scope) { Some(s) => s, None => { self.emit(DiagBuilder2::bug(format!( "scope {:?} does not exist`", scope ))); return Err(()); } }; f(scp) } /// Create a subscope of another scope. pub fn subscope(&self, scope: ScopeRef, parent: ScopeRef) { self.sb .scope2_table .borrow_mut() .insert(scope, Scope::new(Some(parent))); } /// Define a new name in a scope. pub fn define(&self, scope: ScopeRef, name: Spanned, def: Def) -> Result<()> { if self.sess.opts.verbosity.contains(Verbosity::NAMES) { debugln!("define `{}` as {:?} in scope {:?}", name.value, def, scope); } self.with_scope(scope, |scope| match def { // Handle overloadable cases. Def::Enum(_) => { scope .defs .entry(name.value) .or_insert_with(|| Vec::new()) .push(Spanned::new(def, name.span)); Ok(()) } // Handle unique cases. _ => { let ins = scope .defs .insert(name.value, vec![Spanned::new(def, name.span)]); if let Some(existing) = ins { self.emit( DiagBuilder2::error(format!("`{}` has already been declared", name.value)) .span(name.span) .add_note("Previous declaration was here:") .span(existing.last().unwrap().span), ); Err(()) } else { Ok(()) } } }) } /// Import a definition into a scope. pub fn import_def( &self, scope: ScopeRef, name: Spanned, def: Def, ) -> Result<()> { self.with_scope(scope, |scope| { scope .imported_defs .entry(name.value) .or_insert_with(|| Vec::new()) .push(Spanned::new(def, name.span)); Ok(()) }) } /// Import an entire scope into another scope. pub fn import_scope(&self, scope: ScopeRef, into: ScopeRef) -> Result<()> { self.with_scope(into, |into| { into.imported_scopes.insert(scope); Ok(()) }) } }