1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// Copyright (c) 2016-2020 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<ScopeRef>,

    /// The definitions made in this scope.
    pub defs: HashMap<ResolvableName, Vec<Spanned<Def>>>,

    /// The definitions imported from other scopes.
    pub imported_defs: HashMap<ResolvableName, Vec<Spanned<Def>>>,

    /// The explicitly imported scopes.
    pub imported_scopes: HashSet<ScopeRef>,
}

impl Scope {
    /// Create a new empty scope.
    pub fn new(parent: Option<ScopeRef>) -> 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<F, R>(&self, scope: ScopeRef, f: F) -> Result<R>
    where
        F: FnOnce(&mut Scope) -> Result<R>,
    {
        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<ResolvableName>, 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<ResolvableName>,
        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(())
        })
    }
}