moore-vhdl 0.10.0

The VHDL implementation of the moore compiler framework.
Documentation
// Copyright (c) 2016-2020 Fabian Schuiki

//! A compiler pass that gathers definitions.

use crate::hir;
use crate::score::*;
use crate::syntax::ast;
use moore_common::errors::*;
use moore_common::score::Result;
use moore_common::source::*;
#[deny(missing_docs)]
use std::collections::HashMap;

/// A context to declare things in.
///
/// This context helps gather the definitions in a scope. It accepts definitions
/// and keeps track of errors that occurred. Once done the context can be
/// converted into the actual definitions, which fails in case of errors.
pub struct DefsContext<'sbc, 'lazy: 'sbc, 'sb: 'lazy, 'ast: 'sb, 'ctx: 'sb> {
    /// The parent context.
    ctx: &'sbc ScoreContext<'lazy, 'sb, 'ast, 'ctx>,
    /// The definitions made.
    defs: Defs,
    /// Whether any of the declarations caused an error.
    failed: bool,
}

impl<'sbc, 'lazy, 'sb, 'ast, 'ctx> DefsContext<'sbc, 'lazy, 'sb, 'ast, 'ctx> {
    /// Create a new definition context.
    pub fn new(
        ctx: &'sbc ScoreContext<'lazy, 'sb, 'ast, 'ctx>,
    ) -> DefsContext<'sbc, 'lazy, 'sb, 'ast, 'ctx> {
        DefsContext {
            ctx: ctx,
            defs: HashMap::new(),
            failed: false,
        }
    }

    /// Consume the context and return the definitions that were made.
    ///
    /// Returns an error if an error occurred during the earlier gathering of
    /// definitions.
    pub fn finish(self) -> Result<Defs> {
        if self.failed {
            Err(())
        } else {
            Ok(self.defs)
        }
    }

    /// Emit a diagnostic message.
    pub fn emit(&mut self, diag: DiagBuilder2) {
        if diag.severity >= Severity::Error {
            self.failed = true;
        }
        self.ctx.sess.emit(diag)
    }

    /// Declare a name in the scope.
    pub fn declare(&mut self, name: Spanned<ResolvableName>, def: Def) {
        if self.ctx.sess.opts.trace_scoreboard {
            debugln!("[SB][VHDL][SCOPE] declaring `{}` as {:?}", name.value, def);
        }
        match def {
            // Handle overloadable cases.
            Def::Enum(_) => {
                self.defs
                    .entry(name.value)
                    .or_insert_with(|| Vec::new())
                    .push(Spanned::new(def, name.span));
            }

            // Handle unique cases.
            _ => {
                let ins = self
                    .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),
                    );
                }
            }
        }
    }

    /// Declare a primary name in the scope.
    ///
    /// This converts the name to a `ResolvableName` and calls `declare()`.
    pub fn declare_primary_name(&mut self, name: &ast::PrimaryName, def: Def) {
        match self.ctx.resolvable_from_primary_name(name) {
            Ok(n) => self.declare(n, def),
            Err(()) => self.failed = true,
        }
    }

    /// Handle package declarations.
    pub fn declare_pkg(&mut self, id: PkgDeclRef) {
        self.declare(self.ctx.ast(id).1.name.map_into(), Def::Pkg(id))
    }

    /// Handle package instantiations.
    pub fn declare_pkg_inst(&mut self, id: PkgInstRef) {
        self.declare(self.ctx.ast(id).1.name.map_into(), Def::PkgInst(id))
    }

    /// Handle type declarations.
    pub fn declare_type(&mut self, id: TypeDeclRef) {
        let ast = self.ctx.ast(id).1;
        self.declare(ast.name.map_into(), Def::Type(id));
        // This is a rather hacky way of declaring the variant names for enum
        // literals, but it does not require the HIR to be constructed, which is
        // a requirement to avoid infinite loops.
        match ast.data {
            Some(Spanned {
                value: ast::EnumType(ref paren_elems),
                ..
            }) => {
                for (i, lit) in paren_elems.value.iter().enumerate() {
                    match lit.expr.data {
                        ast::NameExpr(ref name) => self.declare(
                            match self.ctx.resolvable_from_primary_name(&name.primary) {
                                Ok(n) => n,
                                Err(()) => continue,
                            },
                            Def::Enum(EnumRef(id, i)),
                        ),
                        _ => (),
                    }
                }
            }
            Some(Spanned {
                value: ast::RangeType(_, Some(ref units)),
                ..
            }) => {
                for (i, unit) in units.iter().enumerate() {
                    self.declare(
                        Spanned::new(unit.0.name.into(), unit.0.span),
                        Def::Unit(UnitRef(id, i)),
                    );
                }
            }
            _ => (),
        }
    }

    /// Handle subtype declarations.
    pub fn declare_subtype(&mut self, id: SubtypeDeclRef) {
        self.declare(self.ctx.ast(id).1.name.map_into(), Def::Subtype(id))
    }

    /// Handle any of the declarations that can appear in a block.
    pub fn declare_any_in_block(&mut self, id: DeclInBlockRef) {
        match id {
            DeclInBlockRef::Subprog(id) => self.declare_subprog(id),
            DeclInBlockRef::SubprogInst(id) => self.declare_subprog_inst(id),
            DeclInBlockRef::SubprogBody(_id) => (),
            DeclInBlockRef::Pkg(id) => self.declare_pkg(id),
            DeclInBlockRef::PkgInst(id) => self.declare_pkg_inst(id),
            DeclInBlockRef::PkgBody(_id) => (),
            DeclInBlockRef::Type(id) => self.declare_type(id),
            DeclInBlockRef::Subtype(id) => self.declare_subtype(id),
            DeclInBlockRef::Const(id) => self.declare_const(id),
            DeclInBlockRef::Signal(id) => self.declare_signal(id),
            DeclInBlockRef::Var(id) => self.declare_var(id),
            DeclInBlockRef::File(id) => self.declare_file(id),
            DeclInBlockRef::Alias(id) => self.declare_alias(id),
            DeclInBlockRef::Comp(id) => self.declare_comp(id),
            DeclInBlockRef::Attr(id) => self.declare_attr(id),
            DeclInBlockRef::AttrSpec(_id) => (),
            DeclInBlockRef::CfgSpec(_id) => (),
            DeclInBlockRef::Discon(_id) => (),
            DeclInBlockRef::GroupTemp(id) => self.declare_group_temp(id),
            DeclInBlockRef::Group(id) => self.declare_group(id),
        }
    }

    /// Handle any of the declarations that can appear in a package.
    pub fn declare_any_in_pkg(&mut self, id: DeclInPkgRef) {
        match id {
            DeclInPkgRef::Subprog(id) => self.declare_subprog(id),
            DeclInPkgRef::SubprogInst(id) => self.declare_subprog_inst(id),
            DeclInPkgRef::Pkg(id) => self.declare_pkg(id),
            DeclInPkgRef::PkgInst(id) => self.declare_pkg_inst(id),
            DeclInPkgRef::Type(id) => self.declare_type(id),
            DeclInPkgRef::Subtype(id) => self.declare_subtype(id),
            DeclInPkgRef::Const(id) => self.declare_const(id),
            DeclInPkgRef::Signal(id) => self.declare_signal(id),
            DeclInPkgRef::Var(id) => self.declare_var(id),
            DeclInPkgRef::File(id) => self.declare_file(id),
            DeclInPkgRef::Alias(id) => self.declare_alias(id),
            DeclInPkgRef::Comp(id) => self.declare_comp(id),
            DeclInPkgRef::Attr(id) => self.declare_attr(id),
            DeclInPkgRef::AttrSpec(_id) => (),
            DeclInPkgRef::Discon(_id) => (),
            DeclInPkgRef::GroupTemp(id) => self.declare_group_temp(id),
            DeclInPkgRef::Group(id) => self.declare_group(id),
        }
    }

    /// Handle any of the declarations that can appear in a package.
    pub fn declare_any_in_pkg_body(&mut self, id: DeclInPkgBodyRef) {
        match id {
            DeclInPkgBodyRef::Subprog(id) => self.declare_subprog(id),
            DeclInPkgBodyRef::SubprogBody(_id) => (),
            DeclInPkgBodyRef::SubprogInst(id) => self.declare_subprog_inst(id),
            DeclInPkgBodyRef::Pkg(id) => self.declare_pkg(id),
            DeclInPkgBodyRef::PkgBody(_id) => (),
            DeclInPkgBodyRef::PkgInst(id) => self.declare_pkg_inst(id),
            DeclInPkgBodyRef::Type(id) => self.declare_type(id),
            DeclInPkgBodyRef::Subtype(id) => self.declare_subtype(id),
            DeclInPkgBodyRef::Const(id) => self.declare_const(id),
            DeclInPkgBodyRef::Var(id) => self.declare_var(id),
            DeclInPkgBodyRef::File(id) => self.declare_file(id),
            DeclInPkgBodyRef::Alias(id) => self.declare_alias(id),
            DeclInPkgBodyRef::Attr(id) => self.declare_attr(id),
            DeclInPkgBodyRef::AttrSpec(_id) => (),
            DeclInPkgBodyRef::GroupTemp(id) => self.declare_group_temp(id),
            DeclInPkgBodyRef::Group(id) => self.declare_group(id),
        }
    }

    /// Handle any of the declarations that can appear in a subprogram.
    pub fn declare_any_in_subprog(&mut self, id: DeclInSubprogRef) {
        match id {
            DeclInSubprogRef::Subprog(id) => self.declare_subprog(id),
            DeclInSubprogRef::SubprogBody(_id) => (),
            DeclInSubprogRef::SubprogInst(id) => self.declare_subprog_inst(id),
            DeclInSubprogRef::Pkg(id) => self.declare_pkg(id),
            DeclInSubprogRef::PkgBody(_id) => (),
            DeclInSubprogRef::PkgInst(id) => self.declare_pkg_inst(id),
            DeclInSubprogRef::Type(id) => self.declare_type(id),
            DeclInSubprogRef::Subtype(id) => self.declare_subtype(id),
            DeclInSubprogRef::Const(id) => self.declare_const(id),
            DeclInSubprogRef::Var(id) => self.declare_var(id),
            DeclInSubprogRef::File(id) => self.declare_file(id),
            DeclInSubprogRef::Alias(id) => self.declare_alias(id),
            DeclInSubprogRef::Attr(id) => self.declare_attr(id),
            DeclInSubprogRef::AttrSpec(_id) => (),
            DeclInSubprogRef::GroupTemp(id) => self.declare_group_temp(id),
            DeclInSubprogRef::Group(id) => self.declare_group(id),
        }
    }

    /// Handle any of the declarations that can appear in a process.
    pub fn declare_any_in_process(&mut self, id: DeclInProcRef) {
        match id {
            DeclInProcRef::Subprog(id) => self.declare_subprog(id),
            DeclInProcRef::SubprogInst(id) => self.declare_subprog_inst(id),
            DeclInProcRef::SubprogBody(_id) => (),
            DeclInProcRef::Pkg(id) => self.declare_pkg(id),
            DeclInProcRef::PkgInst(id) => self.declare_pkg_inst(id),
            DeclInProcRef::PkgBody(_id) => (),
            DeclInProcRef::Type(id) => self.declare_type(id),
            DeclInProcRef::Subtype(id) => self.declare_subtype(id),
            DeclInProcRef::Const(id) => self.declare_const(id),
            DeclInProcRef::Var(id) => self.declare_var(id),
            DeclInProcRef::File(id) => self.declare_file(id),
            DeclInProcRef::Alias(id) => self.declare_alias(id),
            DeclInProcRef::Attr(id) => self.declare_attr(id),
            DeclInProcRef::AttrSpec(_id) => (),
            DeclInProcRef::GroupTemp(id) => self.declare_group_temp(id),
            DeclInProcRef::Group(id) => self.declare_group(id),
        }
    }

    /// Handle a constant declaration.
    pub fn declare_const(&mut self, id: ConstDeclRef) {
        let hir = match self.ctx.lazy_hir(id) {
            Ok(h) => h,
            Err(()) => {
                self.failed = true;
                return;
            }
        };
        self.declare(hir.name.map_into(), Def::Const(id.into()))
    }

    /// Handle a signal declaration.
    pub fn declare_signal(&mut self, id: SignalDeclRef) {
        let hir = match self.ctx.lazy_hir(id) {
            Ok(h) => h,
            Err(()) => {
                self.failed = true;
                return;
            }
        };
        self.declare(hir.name.map_into(), Def::Signal(id.into()))
    }

    /// Handle a variable declaration.
    pub fn declare_var(&mut self, id: VarDeclRef) {
        let hir = match self.ctx.lazy_hir(id) {
            Ok(h) => h,
            Err(()) => {
                self.failed = true;
                return;
            }
        };
        self.declare(hir.name.map_into(), Def::Var(id.into()))
    }

    /// Handle a file declaration.
    pub fn declare_file(&mut self, id: FileDeclRef) {
        let hir = match self.ctx.lazy_hir(id) {
            Ok(h) => h,
            Err(()) => {
                self.failed = true;
                return;
            }
        };
        self.declare(hir.name.map_into(), Def::File(id.into()))
    }

    /// Handle an alias declaration.
    pub fn declare_alias(&mut self, id: AliasDeclRef) {
        self.declare_primary_name(&self.ctx.ast(id).1.name, Def::Alias(id))
    }

    /// Handle a component declaration.
    pub fn declare_comp(&mut self, id: CompDeclRef) {
        self.declare(self.ctx.ast(id).1.name.map_into(), Def::Comp(id))
    }

    /// Handle an attribute declaration.
    pub fn declare_attr(&mut self, id: AttrDeclRef) {
        self.declare(self.ctx.ast(id).1.name.map_into(), Def::Attr(id))
    }

    /// Handle a group template declaration.
    pub fn declare_group_temp(&mut self, id: GroupTempRef) {
        self.declare(self.ctx.ast(id).1.name.map_into(), Def::GroupTemp(id))
    }

    /// Handle a group declaration.
    pub fn declare_group(&mut self, id: GroupDeclRef) {
        self.declare(self.ctx.ast(id).1.name.map_into(), Def::Group(id))
    }

    /// Handle subprogram declarations.
    pub fn declare_subprog(&mut self, id: SubprogDeclRef) {
        self.declare_primary_name(&self.ctx.ast(id).1.spec.name, Def::Subprog(id))
    }

    /// Handle subprogram instantiations.
    pub fn declare_subprog_inst(&mut self, id: SubprogInstRef) {
        self.declare_primary_name(&self.ctx.ast(id).1.spec.name, Def::SubprogInst(id))
    }

    /// Handle subprogram specifications.
    ///
    /// Note that this does not declare the subprogram itself, but rather its
    /// parameters and generics.
    pub fn declare_subprog_spec(&mut self, hir: &hir::SubprogSpec) {
        self.declare_generics(&hir.generics);
        self.declare_intf_objs(&hir.params);
    }

    /// Handle interface objects.
    ///
    /// These are mainly subprogram parameters and entity ports.
    pub fn declare_intf_objs(&mut self, ids: &[IntfObjRef]) {
        if !ids.is_empty() {
            unimplemented!();
        }
    }

    /// Handle generics.
    pub fn declare_generics(&mut self, ids: &[GenericRef]) {
        if !ids.is_empty() {
            unimplemented!();
        }
    }
}