use crate::ast::NetType::GROUND;
use crate::ast_lowering::ast_to_hir_fold::Fold;
use crate::ast_lowering::error::{Error, NetInfo, Type};
use crate::hir::Net;
use crate::hir::{Branch, BranchDeclaration, DisciplineAccess};
use crate::ir::*;
use crate::symbol::{keywords, Ident};
use crate::{ast, Ast, Span};
use rustc_hash::FxHashMap;
pub struct BranchResolver {
unnamed_branches: FxHashMap<(NetId, NetId), BranchId>,
unnamed_port_branches: FxHashMap<PortId, BranchId>,
implicit_grounds: FxHashMap<DisciplineId, NetId>,
}
impl<'lt> BranchResolver {
pub fn new(ast: &'lt Ast) -> Self {
Self {
unnamed_port_branches: FxHashMap::with_capacity_and_hasher(16, Default::default()),
unnamed_branches: FxHashMap::with_capacity_and_hasher(32, Default::default()),
implicit_grounds: FxHashMap::with_capacity_and_hasher(
ast.disciplines.len() as usize,
Default::default(),
),
}
}
pub fn resolve_discipline_access(
&mut self,
fold: &mut Fold<'lt>,
nature_name: &Ident,
discipline: DisciplineId,
) -> Option<DisciplineAccess> {
match nature_name.name {
keywords::FLOW => Some(DisciplineAccess::Flow),
keywords::POTENTIAL => Some(DisciplineAccess::Potential),
_ => {
resolve!(fold; nature_name as
Nature(id) => {
return Self::resolve_nature_access(fold,id,discipline);
}
);
None
}
}
}
pub fn resolve_nature_access(
fold: &mut Fold<'lt>,
id: NatureId,
discipline: DisciplineId,
) -> Option<DisciplineAccess> {
match id {
id if Some(id) == fold.hir[discipline].contents.flow_nature => {
Some(DisciplineAccess::Flow)
}
id if Some(id) == fold.hir[discipline].contents.potential_nature => {
Some(DisciplineAccess::Potential)
}
_ => {
fold.error(Error {
source: fold.hir[id].contents.name.span,
error_type: Type::NatureNotPotentialOrFlow(
fold.hir[id].contents.name.name,
discipline,
),
});
None
}
}
}
pub fn resolve_branch_access(
&mut self,
fold: &mut Fold<'lt>,
branch_access: &Node<ast::BranchAccess>,
) -> Option<(BranchId, DisciplineId)> {
match branch_access.contents {
ast::BranchAccess::Implicit(ref branch) => {
let (branch, discipline) = self.resolve_branch(fold, branch)?;
match branch {
Branch::Port(port) => match self.unnamed_port_branches.get(&port) {
Some(id) => return Some((*id, discipline)),
None => {
let branch_id = fold.hir.branches.push(AttributeNode {
attributes: Attributes::empty(),
source: branch_access.source,
contents: BranchDeclaration {
name: Ident::from_str_and_span(
format!(
"( <{}> )",
fold.hir[fold.hir[port].net].contents.name
)
.as_str(),
branch_access.source,
),
branch,
},
});
self.unnamed_port_branches.insert(port, branch_id);
return Some((branch_id, discipline));
}
},
Branch::Nets(net1, net2) => match self.unnamed_branches.get(&(net1, net2)) {
Some(id) => return Some((*id, discipline)),
None => {
let branch_id = fold.hir.branches.push(AttributeNode {
attributes: Attributes::empty(),
source: branch_access.source,
contents: BranchDeclaration {
name: Ident::from_str_and_span(
format!(
"({} , {})",
fold.hir[net1].contents.name.name.as_str(),
fold.hir[net2].contents.name.name.as_str()
)
.as_str(),
branch_access.source,
),
branch,
},
});
self.unnamed_branches.insert((net1, net2), branch_id);
return Some((branch_id, discipline));
}
},
}
}
ast::BranchAccess::BranchOrNodePotential(ref name) => {
resolve_hierarchical!(fold; name as
Branch(id) => {
let discipline = match fold.hir[id].contents.branch {
Branch::Port(portid) => {
fold.hir[fold.hir[portid].net].contents.discipline
}
Branch::Nets(net1, _) => fold.hir[net1].contents.discipline
};
return Some((id,discipline))
},
Port(_id) => {
return self.resolve_branch_access(fold,&branch_access.clone_as(ast::BranchAccess::Implicit(ast::Branch::Port(name.clone()))))
},
Net(_id) => {
return self.resolve_branch_access(fold,&branch_access.clone_as(ast::BranchAccess::Implicit(ast::Branch::NetToGround(name.clone()))))
}
)
}
}
None
}
pub fn resolve_branch(
&mut self,
fold: &mut Fold<'lt>,
branch: &ast::Branch,
) -> Option<(Branch, DisciplineId)> {
match branch {
ast::Branch::Port(ref port) => {
resolve_hierarchical!(fold; port as Port(port_id) => {
return Some((Branch::Port(port_id),fold.hir[fold.hir[port_id].net].contents.discipline));
});
}
ast::Branch::NetToGround(ref net_ident) => {
let mut net = None;
resolve_hierarchical!(fold; net_ident as
Net(id) => {
net = Some(id);
},
Port(id) => {
net = Some(fold.hir[id].net);
}
);
if let Some(net) = net {
let discipline = fold.hir[net].contents.discipline;
let ground_net =
*self.implicit_grounds.entry(discipline).or_insert_with(|| {
fold.hir.nets.push(AttributeNode {
contents: Net {
name: Ident::from_str(
"implicit_ground",
),
discipline,
signed: false,
net_type: GROUND,
},
source: Span::new_short_empty_span(0),
attributes: Attributes::empty(),
})
});
return Some((Branch::Nets(net, ground_net), discipline));
}
}
ast::Branch::Nets(ref net1, ref net2) => {
let mut first_net = None;
resolve_hierarchical!(fold; net1 as
Net(id) => {
first_net = Some(id);
},
Port(id) => {
first_net = Some(fold.hir[id].net);
}
);
let mut second_net = None;
resolve_hierarchical!(fold; net2 as
Net(second_id) => {
second_net = Some(second_id)
},
Port(second_id) => {
second_net = Some(fold.hir[second_id].net)
}
);
if let (Some(first_net), Some(second_net)) = (first_net, second_net) {
if fold.hir[first_net].contents.discipline
== fold.hir[second_net].contents.discipline
{
return Some((
Branch::Nets(first_net, second_net),
fold.hir[first_net].contents.discipline,
))
;
} else { fold.error(Error {
error_type: Type::DisciplineMismatch(
NetInfo {
discipline: fold.hir[first_net].contents.discipline,
name: fold.hir[first_net].contents.name.name,
declaration: fold.hir[first_net].source,
},
NetInfo {
discipline: fold.hir[second_net].contents.discipline,
name: fold.hir[second_net].contents.name.name,
declaration: fold.hir[second_net].source,
},
),
source: net1.span().extend(net2.span()),
})
}
}
}
}
None
}
}