open_vaf/ast_lowering/branch_resolution.rs
1/*
2 * ******************************************************************************************
3 * Copyright (c) 2019 Pascal Kuthe. This file is part of the OpenVAF project.
4 * It is subject to the license terms in the LICENSE file found in the top-level directory
5 * of this distribution and at https://gitlab.com/DSPOM/OpenVAF/blob/master/LICENSE.
6 * No part of OpenVAF, including this file, may be copied, modified, propagated, or
7 * distributed except according to the terms contained in the LICENSE file.
8 * *****************************************************************************************
9 */
10
11use crate::ast::NetType::GROUND;
12use crate::ast_lowering::ast_to_hir_fold::Fold;
13use crate::ast_lowering::error::{Error, NetInfo, Type};
14use crate::hir::Net;
15use crate::hir::{Branch, BranchDeclaration, DisciplineAccess};
16use crate::ir::*;
17use crate::symbol::{keywords, Ident};
18use crate::{ast, Ast, Span};
19use rustc_hash::FxHashMap;
20
21/// Handles branch resolution which is more complicated because unnamed branches exist and discipline comparability has to be enforced
22/// # Safety
23/// Branch resolution may only take place after all nets and ports have been folded! UB might occur otherwise.
24pub struct BranchResolver {
25 unnamed_branches: FxHashMap<(NetId, NetId), BranchId>,
26 unnamed_port_branches: FxHashMap<PortId, BranchId>,
27 implicit_grounds: FxHashMap<DisciplineId, NetId>,
28}
29
30impl<'lt> BranchResolver {
31 pub fn new(ast: &'lt Ast) -> Self {
32 Self {
33 unnamed_port_branches: FxHashMap::with_capacity_and_hasher(16, Default::default()),
34 unnamed_branches: FxHashMap::with_capacity_and_hasher(32, Default::default()),
35 implicit_grounds: FxHashMap::with_capacity_and_hasher(
36 ast.disciplines.len() as usize,
37 Default::default(),
38 ),
39 }
40 }
41 /// Resolves a DisciplineAccess (for example `V(b)` or `V(x,y)`)
42 ///
43 /// # Arguments
44 ///
45 /// * fold - The calling fold which is used for name resolution and error handling
46 ///
47 /// * nature_name - The identifier of the nature (for example `V` in the case of `V(X,Y)`)
48 ///
49 /// * discipline - The id of the Discipline of a BranchAccess ( that has been resolved using [`resolve_branch_access`](crate::ast_lowering::branch_resolution::BranchResolver::resolve_discipline_access)
50 ///
51 ///
52 /// # Safety
53 /// This is only save to call
54 ///
55 ///
56 pub fn resolve_discipline_access(
57 &mut self,
58 fold: &mut Fold<'lt>,
59 nature_name: &Ident,
60 discipline: DisciplineId,
61 ) -> Option<DisciplineAccess> {
62 match nature_name.name {
63 keywords::FLOW => Some(DisciplineAccess::Flow),
64 keywords::POTENTIAL => Some(DisciplineAccess::Potential),
65 _ => {
66 resolve!(fold; nature_name as
67 Nature(id) => {
68 return Self::resolve_nature_access(fold,id,discipline);
69 }
70 );
71 None
72 }
73 }
74 }
75
76 pub fn resolve_nature_access(
77 fold: &mut Fold<'lt>,
78 id: NatureId,
79 discipline: DisciplineId,
80 ) -> Option<DisciplineAccess> {
81 match id {
82 id if Some(id) == fold.hir[discipline].contents.flow_nature => {
83 Some(DisciplineAccess::Flow)
84 }
85 id if Some(id) == fold.hir[discipline].contents.potential_nature => {
86 Some(DisciplineAccess::Potential)
87 }
88 _ => {
89 fold.error(Error {
90 source: fold.hir[id].contents.name.span,
91 error_type: Type::NatureNotPotentialOrFlow(
92 fold.hir[id].contents.name.name,
93 discipline,
94 ),
95 });
96 None
97 }
98 }
99 }
100
101 /// Resolves a branch access such as `(NET1,NET2)`,`(<PORT>)` or `(BRANCH)`
102 ///
103 /// # Arguments
104 ///
105 /// * fold - The calling fold which is used for name resolution and error handling
106 ///
107 /// * branch_access - A reference to an Ast node for a branch access call
108 ///
109 ///
110 /// # Returns
111 /// The Id of the resolved branch and its Discipline (if the resolution succeeded)
112 pub fn resolve_branch_access(
113 &mut self,
114 fold: &mut Fold<'lt>,
115 branch_access: &Node<ast::BranchAccess>,
116 ) -> Option<(BranchId, DisciplineId)> {
117 match branch_access.contents {
118 ast::BranchAccess::Implicit(ref branch) => {
119 let (branch, discipline) = self.resolve_branch(fold, branch)?;
120 match branch {
121 Branch::Port(port) => match self.unnamed_port_branches.get(&port) {
122 Some(id) => return Some((*id, discipline)),
123 None => {
124 let branch_id = fold.hir.branches.push(AttributeNode {
125 attributes: Attributes::empty(),
126 source: branch_access.source,
127 contents: BranchDeclaration {
128 name: Ident::from_str_and_span(
129 format!(
130 "( <{}> )",
131 fold.hir[fold.hir[port].net].contents.name
132 )
133 .as_str(),
134 branch_access.source,
135 ),
136 branch,
137 },
138 });
139 self.unnamed_port_branches.insert(port, branch_id);
140 return Some((branch_id, discipline));
141 }
142 },
143
144 Branch::Nets(net1, net2) => match self.unnamed_branches.get(&(net1, net2)) {
145 Some(id) => return Some((*id, discipline)),
146 None => {
147 let branch_id = fold.hir.branches.push(AttributeNode {
148 attributes: Attributes::empty(),
149 source: branch_access.source,
150 contents: BranchDeclaration {
151 name: Ident::from_str_and_span(
152 format!(
153 "({} , {})",
154 fold.hir[net1].contents.name.name.as_str(),
155 fold.hir[net2].contents.name.name.as_str()
156 )
157 .as_str(),
158 branch_access.source,
159 ),
160 branch,
161 },
162 });
163 self.unnamed_branches.insert((net1, net2), branch_id);
164 return Some((branch_id, discipline));
165 }
166 },
167 }
168 }
169
170 ast::BranchAccess::BranchOrNodePotential(ref name) => {
171 resolve_hierarchical!(fold; name as
172 Branch(id) => {
173 let discipline = match fold.hir[id].contents.branch {
174 Branch::Port(portid) => {
175 fold.hir[fold.hir[portid].net].contents.discipline
176 }
177 Branch::Nets(net1, _) => fold.hir[net1].contents.discipline
178 };
179 return Some((id,discipline))
180 },
181
182 // Needed to resolve ambiguities. Inefficient but will do
183 Port(_id) => {
184 return self.resolve_branch_access(fold,&branch_access.clone_as(ast::BranchAccess::Implicit(ast::Branch::Port(name.clone()))))
185 },
186
187 Net(_id) => {
188 return self.resolve_branch_access(fold,&branch_access.clone_as(ast::BranchAccess::Implicit(ast::Branch::NetToGround(name.clone()))))
189 }
190 )
191 }
192 }
193
194 None
195 }
196
197 /// Resolves a branch such as (NET1,NET2) or (<PORT>)
198 ///
199 /// # Arguments
200 ///
201 /// * fold - The calling fold which is used for name resolution and error handling
202 ///
203 /// * branch - An Ast node describing a branch
204 ///
205 ///
206 /// # Returns
207 /// The Id of the resolved branch and its Discipline (if the resolution succeeded)
208
209 pub fn resolve_branch(
210 &mut self,
211 fold: &mut Fold<'lt>,
212 branch: &ast::Branch,
213 ) -> Option<(Branch, DisciplineId)> {
214 match branch {
215 ast::Branch::Port(ref port) => {
216 resolve_hierarchical!(fold; port as Port(port_id) => {
217 return Some((Branch::Port(port_id),fold.hir[fold.hir[port_id].net].contents.discipline));
218 });
219 }
220 ast::Branch::NetToGround(ref net_ident) => {
221 let mut net = None;
222 resolve_hierarchical!(fold; net_ident as
223 Net(id) => {
224 net = Some(id);
225 },
226 Port(id) => {
227 net = Some(fold.hir[id].net);
228 }
229 );
230 if let Some(net) = net {
231 let discipline = fold.hir[net].contents.discipline;
232 let ground_net =
233 *self.implicit_grounds.entry(discipline).or_insert_with(|| {
234 fold.hir.nets.push(AttributeNode {
235 contents: Net {
236 name: Ident::from_str(
237 "implicit_ground",
238 ),
239 discipline,
240 signed: false,
241 net_type: GROUND,
242 },
243 source: Span::new_short_empty_span(0),
244 attributes: Attributes::empty(),
245 })
246 });
247 return Some((Branch::Nets(net, ground_net), discipline));
248 }
249 }
250
251 ast::Branch::Nets(ref net1, ref net2) => {
252 let mut first_net = None;
253 resolve_hierarchical!(fold; net1 as
254 Net(id) => {
255 first_net = Some(id);
256 },
257 Port(id) => {
258 first_net = Some(fold.hir[id].net);
259 }
260 );
261
262 let mut second_net = None;
263 resolve_hierarchical!(fold; net2 as
264 Net(second_id) => {
265 second_net = Some(second_id)
266 },
267 Port(second_id) => {
268 second_net = Some(fold.hir[second_id].net)
269 }
270 );
271
272 if let (Some(first_net), Some(second_net)) = (first_net, second_net) {
273 if fold.hir[first_net].contents.discipline
274 == fold.hir[second_net].contents.discipline
275 {
276 //doesn't matter which nets discipline we use since we asserted that they are equal
277 return Some((
278 Branch::Nets(first_net, second_net),
279 fold.hir[first_net].contents.discipline,
280 ))
281;
282 } else { fold.error(Error {
283 error_type: Type::DisciplineMismatch(
284 NetInfo {
285 discipline: fold.hir[first_net].contents.discipline,
286 name: fold.hir[first_net].contents.name.name,
287 declaration: fold.hir[first_net].source,
288 },
289 NetInfo {
290 discipline: fold.hir[second_net].contents.discipline,
291 name: fold.hir[second_net].contents.name.name,
292 declaration: fold.hir[second_net].source,
293 },
294 ),
295 source: net1.span().extend(net2.span()),
296 })
297
298 }
299 }
300 }
301 }
302
303 None
304 }
305}