Coverage for pyVHDLModel/Regions.py: 94%

910 statements  

« prev     ^ index     » next       coverage.py v7.17.0, created at 2026-10-22 19:31 +0000

1# ==================================================================================================================== # 

2# __ ___ _ ____ _ __ __ _ _ # 

3# _ __ _ \ \ / / | | | _ \| | | \/ | ___ __| | ___| | # 

4# | '_ \| | | \ \ / /| |_| | | | | | | |\/| |/ _ \ / _` |/ _ \ | # 

5# | |_) | |_| |\ V / | _ | |_| | |___| | | | (_) | (_| | __/ | # 

6# | .__/ \__, | \_/ |_| |_|____/|_____|_| |_|\___/ \__,_|\___|_| # 

7# |_| |___/ # 

8# ==================================================================================================================== # 

9# Authors: # 

10# Patrick Lehmann # 

11# # 

12# License: # 

13# ==================================================================================================================== # 

14# Copyright 2017-2026 Patrick Lehmann - Boetzingen, Germany # 

15# Copyright 2016-2017 Patrick Lehmann - Dresden, Germany # 

16# # 

17# Licensed under the Apache License, Version 2.0 (the "License"); # 

18# you may not use this file except in compliance with the License. # 

19# You may obtain a copy of the License at # 

20# # 

21# http://www.apache.org/licenses/LICENSE-2.0 # 

22# # 

23# Unless required by applicable law or agreed to in writing, software # 

24# distributed under the License is distributed on an "AS IS" BASIS, # 

25# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # 

26# See the License for the specific language governing permissions and # 

27# limitations under the License. # 

28# # 

29# SPDX-License-Identifier: Apache-2.0 # 

30# ==================================================================================================================== # 

31# 

32""" 

33This module contains parts of an abstract document language model for VHDL. 

34 

35tbd. 

36""" 

37from __future__ import annotations 

38 

39from typing import TYPE_CHECKING, Any, ClassVar, Iterable, Optional as Nullable 

40 

41from pyTooling.Common import getFullyQualifiedName, getTypeName 

42from pyTooling.Decorators import export, readonly 

43from pyTooling.MetaClasses import ExtendedType, expects 

44from pyTooling.Warning import WarningCollector 

45 

46from pyVHDLModel.Base import ModelEntity, NamedEntityMixin, MultipleNamedEntityMixin, identifiersOf 

47from pyVHDLModel.Base import normalizedIdentifiersOf, normalizedDeclaredNamesOf 

48from pyVHDLModel.Exception import NotImplementedWarning, AlreadyInDesignError, NotAChildError 

49from pyVHDLModel.Exception import DuplicateGenericError, DuplicatePortError, DuplicateParameterError 

50from pyVHDLModel.Exception import DuplicateDeclarationError, DuplicateTypeError, DuplicateSubtypeError 

51from pyVHDLModel.Exception import DuplicateConstantError, DuplicateSignalError, DuplicateVariableError 

52from pyVHDLModel.Exception import DuplicateFileError, DuplicateComponentError, DuplicateSubprogramError 

53from pyVHDLModel.Namespace import Namespace 

54from pyVHDLModel.Object import BaseConstant, Constant, DeferredConstant, SharedVariable, File, Variable, Signal 

55if TYPE_CHECKING: 55 ↛ 56line 55 didn't jump to line 56 because the condition on line 55 was never true

56 from pyVHDLModel.Interface import GenericInterfaceItemMixin, PortInterfaceItemMixin, ParameterInterfaceItemMixin 

57 from pyVHDLModel.Interface import PortSignalInterfaceItem 

58 from pyVHDLModel.Type import Subtype, FullType 

59 

60# `pyVHDLModel.Subprogram` imports this module (Subprogram is a sequential declaration region), so 

61# `Function`/`Procedure` are quoted in annotations and imported lazily where they're needed at runtime. 

62# The same holds for the interface item classes of `pyVHDLModel.Interface`. 

63 

64 

65@export 

66class WithGenericsMixin( 

67 metaclass=ExtendedType, 

68 mixin=True, 

69 expects=("_AddToNamespace", "_RemoveFromNamespace", "_ClashingDeclarations") 

70): 

71 """ 

72 A mixin-class for language constructs with a generic clause. 

73 

74 The host class provides ``_AddToNamespace(element)`` and ``_RemoveFromNamespace(element)``, called when a generic is 

75 inserted or removed: :class:`~pyVHDLModel.Regions.DeclarationRegionMixin` updates its namespace, a language construct 

76 without a namespace implements both as no-operations. 

77 

78 The host class also provides ``_ClashingDeclarations(item)``, called before a generic is inserted: it returns the 

79 declarations sharing an identifier with the new item. 

80 

81 Indexing the generics (``_IndexGenericItems()``) needs the host's namespace ``_namespace``; a host without one, e.g. 

82 a component, can't index them. 

83 

84 .. seealso:: 

85 

86 * :class:`Package <pyVHDLModel.DesignUnit.Package>` 

87 * :class:`Entity <pyVHDLModel.DesignUnit.Entity>` 

88 * :class:`Component <pyVHDLModel.DesignUnit.Component>` 

89 * :class:`Subprogram <pyVHDLModel.Subprogram.Subprogram>` 

90 * :class:`Generic group <pyVHDLModel.Interface.GenericGroup>` 

91 """ 

92 _genericItems: list[GenericInterfaceItemMixin] #: List of all generics, in declaration order. 

93 

94 def __init__( 

95 self, 

96 genericItems: Nullable[Iterable[GenericInterfaceItemMixin]] = None, 

97 ) -> None: 

98 """ 

99 Initializes a language construct with a generic clause. 

100 

101 :param genericItems: List of all generics, in declaration order. 

102 """ 

103 self._genericItems = [] 

104 if genericItems is not None: 

105 for item in genericItems: 

106 self._genericItems.append(item) 

107 item.Parent = self 

108 

109 @readonly 

110 def GenericItems(self) -> list[GenericInterfaceItemMixin]: 

111 """ 

112 Read-only property to access the generic items (:attr:`_genericItems`). 

113 

114 :returns: List of generic items. 

115 """ 

116 return self._genericItems 

117 

118 @readonly 

119 def GenericCount(self) -> int: 

120 """ 

121 Read-only property to return the number of generics in :attr:`_genericItems`. 

122 

123 :returns: The generic count. 

124 """ 

125 return len(self._genericItems) 

126 

127 def AddGenericItem(self, genericItem: GenericInterfaceItemMixin) -> None: 

128 """ 

129 Add a generic at the end of the generic clause. 

130 

131 The generic's parent is set to this language construct, and its identifiers are added to the language construct's 

132 namespace, if it has one. 

133 

134 :param genericItem: The generic to add. 

135 :raises ValueError: If parameter 'genericItem' is ``None``. 

136 :raises TypeError: If parameter 'genericItem' is not of type 

137 :class:`~pyVHDLModel.Interface.GenericInterfaceItemMixin`. 

138 :raises AlreadyInDesignError: If parameter 'genericItem' already has a parent. 

139 :raises DuplicateGenericError: If an existing declaration of the language construct shares an identifier 

140 with the generic. 

141 

142 .. seealso:: 

143 

144 :meth:`InsertGenericItem` 

145 |rarr| Insert a generic at a position of the generic clause. 

146 :meth:`RemoveGenericItem` 

147 |rarr| Remove a generic from the generic clause. 

148 """ 

149 self.InsertGenericItem(len(self._genericItems), genericItem) 

150 

151 def InsertGenericItem(self, index: int, genericItem: GenericInterfaceItemMixin) -> None: 

152 """ 

153 Insert a generic at a position of the generic clause. 

154 

155 The generic's parent is set to this language construct, and its identifiers are added to the language construct's 

156 namespace, if it has one. 

157 

158 :param index: Position, from ``0`` (first) to :data:`GenericCount` (append). 

159 A negative index counts from the end, as for :meth:`list.insert`: ``-1`` inserts 

160 before the last generic. 

161 :param genericItem: The generic to insert. 

162 :raises ValueError: If parameter 'index' is ``None``. 

163 :raises TypeError: If parameter 'index' is not of type :class:`int`. 

164 :raises ValueError: If parameter 'index' is out of range. 

165 :raises ValueError: If parameter 'genericItem' is ``None``. 

166 :raises TypeError: If parameter 'genericItem' is not of type 

167 :class:`~pyVHDLModel.Interface.GenericInterfaceItemMixin`. 

168 :raises AlreadyInDesignError: If parameter 'genericItem' already has a parent. 

169 :raises DuplicateGenericError: If an existing declaration of the language construct shares an identifier 

170 with the generic. 

171 

172 .. seealso:: 

173 

174 :meth:`AddGenericItem` 

175 |rarr| Add a generic at the end of the generic clause. 

176 :meth:`RemoveGenericItem` 

177 |rarr| Remove a generic from the generic clause. 

178 :meth:`MoveGenericItem` 

179 |rarr| Move a generic to a position of the generic clause. 

180 """ 

181 from pyVHDLModel.Interface import GenericInterfaceItemMixin 

182 

183 if index is None: 

184 raise ValueError("Parameter 'index' is None.") 

185 elif not isinstance(index, int): 

186 ex = TypeError("Parameter 'index' is not of type 'int'.") 

187 ex.add_note(f"Got type '{getFullyQualifiedName(index)}'.") 

188 raise ex 

189 elif not (-len(self._genericItems) <= index <= len(self._genericItems)): 

190 ex = ValueError(f"Parameter 'index' is out of range {-len(self._genericItems)}..{len(self._genericItems)}.") 

191 ex.add_note(f"Got value '{index}'.") 

192 raise ex 

193 

194 if genericItem is None: 

195 raise ValueError("Parameter 'genericItem' is None.") 

196 elif not isinstance(genericItem, GenericInterfaceItemMixin): 

197 ex = TypeError("Parameter 'genericItem' is not of type 'GenericInterfaceItemMixin'.") 

198 ex.add_note(f"Got type '{getFullyQualifiedName(genericItem)}'.") 

199 raise ex 

200 elif genericItem._parent is not None: 

201 identifiers = ", ".join(identifiersOf(genericItem)) 

202 ex = AlreadyInDesignError( 

203 f"{getTypeName(genericItem)} '{identifiers}' already has a parent.", 

204 element=genericItem, 

205 parent=genericItem._parent 

206 ) 

207 ex.add_note(f"Remove it from its {getTypeName(genericItem._parent)} first.") 

208 raise ex 

209 

210 if len(clashes := self._ClashingDeclarations(genericItem)) > 0: 

211 existingIdentifiers = normalizedIdentifiersOf(clashes[0]) 

212 newIdentifiers = zip(identifiersOf(genericItem), normalizedIdentifiersOf(genericItem)) 

213 identifier = next(name for name, normalizedName in newIdentifiers if normalizedName in existingIdentifiers) 

214 ex = DuplicateGenericError( 

215 f"{getTypeName(clashes[0]._parent)} already declares '{identifier}'.", 

216 identifier=identifier, 

217 parent=self, 

218 element=genericItem, 

219 existing=clashes[0] 

220 ) 

221 ex.add_note(f"Existing declaration is of type '{getFullyQualifiedName(clashes[0])}'.") 

222 raise ex 

223 

224 self._genericItems.insert(index, genericItem) 

225 genericItem.Parent = self 

226 self._AddToNamespace(genericItem) 

227 

228 def RemoveGenericItem(self, genericItem: GenericInterfaceItemMixin) -> None: 

229 """ 

230 Remove a generic from the generic clause. 

231 

232 The generic is detached: its parent is cleared and its identifiers are removed from the language construct's 

233 namespace, if it has one. The generic itself is unchanged and can be added to another language construct, or 

234 inserted at another position. 

235 

236 :param genericItem: The generic to remove. 

237 :raises ValueError: If parameter 'genericItem' is ``None``. 

238 :raises TypeError: If parameter 'genericItem' is not of type 

239 :class:`~pyVHDLModel.Interface.GenericInterfaceItemMixin`. 

240 :raises NotAChildError: If parameter 'genericItem' is not a generic of this language construct. 

241 :raises NotInNamespaceError: If the namespace is indexed, and an identifier of the generic isn't declared in it. 

242 :raises NotInNamespaceError: If the namespace is indexed, and an identifier of the generic refers to another 

243 element, e.g. a duplicate declaration. 

244 

245 .. seealso:: 

246 

247 :meth:`AddGenericItem` 

248 |rarr| Add a generic at the end of the generic clause. 

249 :meth:`InsertGenericItem` 

250 |rarr| Insert a generic at a position of the generic clause. 

251 :meth:`MoveGenericItem` 

252 |rarr| Move a generic to a position of the generic clause. 

253 """ 

254 from pyVHDLModel.Interface import GenericInterfaceItemMixin 

255 

256 if genericItem is None: 

257 raise ValueError("Parameter 'genericItem' is None.") 

258 elif not isinstance(genericItem, GenericInterfaceItemMixin): 

259 ex = TypeError("Parameter 'genericItem' is not of type 'GenericInterfaceItemMixin'.") 

260 ex.add_note(f"Got type '{getFullyQualifiedName(genericItem)}'.") 

261 raise ex 

262 

263 for index, item in enumerate(self._genericItems): 

264 if item is genericItem: 

265 break 

266 else: 

267 identifiers = ", ".join(identifiersOf(genericItem)) 

268 raise NotAChildError( 

269 f"{getTypeName(genericItem)} '{identifiers}' is not a generic of this {getTypeName(self)}.", 

270 element=genericItem, 

271 parent=self 

272 ) 

273 

274 self._RemoveFromNamespace(genericItem) 

275 del self._genericItems[index] 

276 genericItem._parent = None 

277 

278 def MoveGenericItem(self, genericItem: GenericInterfaceItemMixin, index: int) -> None: 

279 """ 

280 Move a generic to a position of the generic clause. 

281 

282 The generic stays a generic of this language construct: only the order of the generic clause changes. A generic 

283 subprogram is an overload: if the language construct's namespace is indexed, it refers to the overload declared 

284 last afterwards. 

285 

286 :param genericItem: The generic to move. 

287 :param index: Position of the generic after the move, from ``0`` (first) to :data:`GenericCount` 

288 minus one (last). A negative index counts from the end, as for a :class:`list` 

289 index: ``-1`` moves the generic to the end. 

290 :raises ValueError: If parameter 'genericItem' is ``None``. 

291 :raises TypeError: If parameter 'genericItem' is not of type 

292 :class:`~pyVHDLModel.Interface.GenericInterfaceItemMixin`. 

293 :raises NotAChildError: If parameter 'genericItem' is not a generic of this language construct. 

294 :raises ValueError: If parameter 'index' is ``None``. 

295 :raises TypeError: If parameter 'index' is not of type :class:`int`. 

296 :raises ValueError: If parameter 'index' is out of range. 

297 :raises NotInNamespaceError: If the namespace is indexed, and the identifier of a generic subprogram refers to an 

298 element, which is no overload, e.g. a duplicate declaration. 

299 

300 .. seealso:: 

301 

302 :meth:`InsertGenericItem` 

303 |rarr| Insert a generic at a position of the generic clause. 

304 :meth:`RemoveGenericItem` 

305 |rarr| Remove a generic from the generic clause. 

306 """ 

307 from pyVHDLModel.Interface import GenericInterfaceItemMixin 

308 from pyVHDLModel.Subprogram import Function, Procedure 

309 

310 if genericItem is None: 

311 raise ValueError("Parameter 'genericItem' is None.") 

312 elif not isinstance(genericItem, GenericInterfaceItemMixin): 

313 ex = TypeError("Parameter 'genericItem' is not of type 'GenericInterfaceItemMixin'.") 

314 ex.add_note(f"Got type '{getFullyQualifiedName(genericItem)}'.") 

315 raise ex 

316 

317 for currentIndex, item in enumerate(self._genericItems): 

318 if item is genericItem: 

319 break 

320 else: 

321 identifiers = ", ".join(identifiersOf(genericItem)) 

322 raise NotAChildError( 

323 f"{getTypeName(genericItem)} '{identifiers}' is not a generic of this {getTypeName(self)}.", 

324 element=genericItem, 

325 parent=self 

326 ) 

327 

328 if index is None: 

329 raise ValueError("Parameter 'index' is None.") 

330 elif not isinstance(index, int): 

331 ex = TypeError("Parameter 'index' is not of type 'int'.") 

332 ex.add_note(f"Got type '{getFullyQualifiedName(index)}'.") 

333 raise ex 

334 elif not (-len(self._genericItems) <= index < len(self._genericItems)): 

335 ex = ValueError(f"Parameter 'index' is out of range {-len(self._genericItems)}..{len(self._genericItems) - 1}.") 

336 ex.add_note(f"Got value '{index}'.") 

337 raise ex 

338 

339 newIndex = index if index >= 0 else len(self._genericItems) + index 

340 if newIndex == currentIndex: 

341 return 

342 

343 isOverload = isinstance(genericItem, (Function, Procedure)) 

344 if isOverload: 

345 self._RemoveFromNamespace(genericItem) 

346 

347 del self._genericItems[currentIndex] 

348 self._genericItems.insert(newIndex, genericItem) 

349 

350 if isOverload: 

351 self._AddToNamespace(genericItem) 

352 

353 @expects("_namespace") 

354 def _IndexGenericItems(self) -> None: 

355 """Add the generics to the host's namespace; a generic subprogram is an overload.""" 

356 from pyVHDLModel.Subprogram import Function, Procedure 

357 

358 for item in self._genericItems: 

359 overloadable = isinstance(item, (Function, Procedure)) 

360 for normalizedIdentifier in normalizedIdentifiersOf(item): 

361 self._namespace.AddElement(normalizedIdentifier, item, overloadable=overloadable) 

362 

363 

364@export 

365class WithPortsMixin( 

366 metaclass=ExtendedType, 

367 mixin=True, 

368 expects=("_AddToNamespace", "_RemoveFromNamespace", "_ClashingDeclarations") 

369): 

370 """ 

371 A mixin-class for language constructs with a port clause. 

372 

373 The host class provides ``_AddToNamespace(element)`` and ``_RemoveFromNamespace(element)``, called when a port is 

374 inserted or removed: :class:`~pyVHDLModel.Regions.DeclarationRegionMixin` updates its namespace, a language construct 

375 without a namespace implements both as no-operations. 

376 

377 The host class also provides ``_ClashingDeclarations(item)``, called before a port is inserted: it returns the 

378 declarations sharing an identifier with the new item. 

379 

380 Indexing the ports (``_IndexPortItems()``) needs the host's namespace ``_namespace``; a host without one, e.g. 

381 a component, can't index them. 

382 

383 .. seealso:: 

384 

385 * :class:`Concurrent block statement <pyVHDLModel.Concurrent.ConcurrentBlockStatement>` 

386 * :class:`Entity <pyVHDLModel.DesignUnit.Entity>` 

387 * :class:`Component <pyVHDLModel.DesignUnit.Component>` 

388 * :class:`Port group <pyVHDLModel.Interface.PortGroup>` 

389 """ 

390 _portItems: list[PortInterfaceItemMixin] #: List of all ports, in declaration order. 

391 

392 def __init__( 

393 self, 

394 portItems: Nullable[Iterable[PortInterfaceItemMixin]] = None, 

395 ) -> None: 

396 """ 

397 Initializes a language construct with a port clause. 

398 

399 :param portItems: List of all ports, in declaration order. 

400 """ 

401 self._portItems = [] 

402 if portItems is not None: 

403 for item in portItems: 

404 self._portItems.append(item) 

405 item.Parent = self 

406 

407 @readonly 

408 def PortItems(self) -> list[PortInterfaceItemMixin]: 

409 """ 

410 Read-only property to access the port items (:attr:`_portItems`). 

411 

412 :returns: List of port items. 

413 """ 

414 return self._portItems 

415 

416 @readonly 

417 def PortCount(self) -> int: 

418 """ 

419 Read-only property to return the number of ports in :attr:`_portItems`. 

420 

421 :returns: The port count. 

422 """ 

423 return len(self._portItems) 

424 

425 def AddPortItem(self, portItem: PortSignalInterfaceItem) -> None: 

426 """ 

427 Add a port at the end of the port clause. 

428 

429 The port's parent is set to this language construct, and its identifiers are added to the language construct's 

430 namespace, if it has one. 

431 

432 :param portItem: The port to add. 

433 :raises ValueError: If parameter 'portItem' is ``None``. 

434 :raises TypeError: If parameter 'portItem' is not of type 

435 :class:`~pyVHDLModel.Interface.PortSignalInterfaceItem`. 

436 :raises AlreadyInDesignError: If parameter 'portItem' already has a parent. 

437 :raises DuplicatePortError: If an existing declaration of the language construct shares an identifier 

438 with the port. 

439 

440 .. seealso:: 

441 

442 :meth:`InsertPortItem` 

443 |rarr| Insert a port at a position of the port clause. 

444 :meth:`RemovePortItem` 

445 |rarr| Remove a port from the port clause. 

446 """ 

447 self.InsertPortItem(len(self._portItems), portItem) 

448 

449 def InsertPortItem(self, index: int, portItem: PortSignalInterfaceItem) -> None: 

450 """ 

451 Insert a port at a position of the port clause. 

452 

453 The port's parent is set to this language construct, and its identifiers are added to the language construct's 

454 namespace, if it has one. 

455 

456 :param index: Position, from ``0`` (first) to :data:`PortCount` (append). 

457 A negative index counts from the end, as for :meth:`list.insert`: ``-1`` inserts 

458 before the last port. 

459 :param portItem: The port to insert. 

460 :raises ValueError: If parameter 'index' is ``None``. 

461 :raises TypeError: If parameter 'index' is not of type :class:`int`. 

462 :raises ValueError: If parameter 'index' is out of range. 

463 :raises ValueError: If parameter 'portItem' is ``None``. 

464 :raises TypeError: If parameter 'portItem' is not of type 

465 :class:`~pyVHDLModel.Interface.PortSignalInterfaceItem`. 

466 :raises AlreadyInDesignError: If parameter 'portItem' already has a parent. 

467 :raises DuplicatePortError: If an existing declaration of the language construct shares an identifier 

468 with the port. 

469 

470 .. seealso:: 

471 

472 :meth:`AddPortItem` 

473 |rarr| Add a port at the end of the port clause. 

474 :meth:`RemovePortItem` 

475 |rarr| Remove a port from the port clause. 

476 :meth:`MovePortItem` 

477 |rarr| Move a port to a position of the port clause. 

478 """ 

479 from pyVHDLModel.Interface import PortSignalInterfaceItem 

480 

481 if index is None: 481 ↛ 482line 481 didn't jump to line 482 because the condition on line 481 was never true

482 raise ValueError("Parameter 'index' is None.") 

483 elif not isinstance(index, int): 483 ↛ 484line 483 didn't jump to line 484 because the condition on line 483 was never true

484 ex = TypeError("Parameter 'index' is not of type 'int'.") 

485 ex.add_note(f"Got type '{getFullyQualifiedName(index)}'.") 

486 raise ex 

487 elif not (-len(self._portItems) <= index <= len(self._portItems)): 

488 ex = ValueError(f"Parameter 'index' is out of range {-len(self._portItems)}..{len(self._portItems)}.") 

489 ex.add_note(f"Got value '{index}'.") 

490 raise ex 

491 

492 if portItem is None: 

493 raise ValueError("Parameter 'portItem' is None.") 

494 elif not isinstance(portItem, PortSignalInterfaceItem): 

495 ex = TypeError("Parameter 'portItem' is not of type 'PortSignalInterfaceItem'.") 

496 ex.add_note(f"Got type '{getFullyQualifiedName(portItem)}'.") 

497 raise ex 

498 elif portItem._parent is not None: 

499 identifiers = ", ".join(identifiersOf(portItem)) 

500 ex = AlreadyInDesignError( 

501 f"{getTypeName(portItem)} '{identifiers}' already has a parent.", 

502 element=portItem, 

503 parent=portItem._parent 

504 ) 

505 ex.add_note(f"Remove it from its {getTypeName(portItem._parent)} first.") 

506 raise ex 

507 

508 if len(clashes := self._ClashingDeclarations(portItem)) > 0: 

509 existingIdentifiers = normalizedIdentifiersOf(clashes[0]) 

510 newIdentifiers = zip(identifiersOf(portItem), normalizedIdentifiersOf(portItem)) 

511 identifier = next(name for name, normalizedName in newIdentifiers if normalizedName in existingIdentifiers) 

512 ex = DuplicatePortError( 

513 f"{getTypeName(clashes[0]._parent)} already declares '{identifier}'.", 

514 identifier=identifier, 

515 parent=self, 

516 element=portItem, 

517 existing=clashes[0] 

518 ) 

519 ex.add_note(f"Existing declaration is of type '{getFullyQualifiedName(clashes[0])}'.") 

520 raise ex 

521 

522 self._portItems.insert(index, portItem) 

523 portItem.Parent = self 

524 self._AddToNamespace(portItem) 

525 

526 def RemovePortItem(self, portItem: PortSignalInterfaceItem) -> None: 

527 """ 

528 Remove a port from the port clause. 

529 

530 The port is detached: its parent is cleared and its identifiers are removed from the language construct's 

531 namespace, if it has one. The port itself is unchanged and can be added to another language construct, or 

532 inserted at another position. 

533 

534 :param portItem: The port to remove. 

535 :raises ValueError: If parameter 'portItem' is ``None``. 

536 :raises TypeError: If parameter 'portItem' is not of type 

537 :class:`~pyVHDLModel.Interface.PortSignalInterfaceItem`. 

538 :raises NotAChildError: If parameter 'portItem' is not a port of this language construct. 

539 :raises NotInNamespaceError: If the namespace is indexed, and an identifier of the port isn't declared in it. 

540 :raises NotInNamespaceError: If the namespace is indexed, and an identifier of the port refers to another 

541 element, e.g. a duplicate declaration. 

542 

543 .. seealso:: 

544 

545 :meth:`AddPortItem` 

546 |rarr| Add a port at the end of the port clause. 

547 :meth:`InsertPortItem` 

548 |rarr| Insert a port at a position of the port clause. 

549 :meth:`MovePortItem` 

550 |rarr| Move a port to a position of the port clause. 

551 """ 

552 from pyVHDLModel.Interface import PortSignalInterfaceItem 

553 

554 if portItem is None: 

555 raise ValueError("Parameter 'portItem' is None.") 

556 elif not isinstance(portItem, PortSignalInterfaceItem): 

557 ex = TypeError("Parameter 'portItem' is not of type 'PortSignalInterfaceItem'.") 

558 ex.add_note(f"Got type '{getFullyQualifiedName(portItem)}'.") 

559 raise ex 

560 

561 for index, item in enumerate(self._portItems): 

562 if item is portItem: 562 ↛ 561line 562 didn't jump to line 561 because the condition on line 562 was always true

563 break 

564 else: 

565 identifiers = ", ".join(identifiersOf(portItem)) 

566 raise NotAChildError( 

567 f"{getTypeName(portItem)} '{identifiers}' is not a port of this {getTypeName(self)}.", 

568 element=portItem, 

569 parent=self 

570 ) 

571 

572 self._RemoveFromNamespace(portItem) 

573 del self._portItems[index] 

574 portItem._parent = None 

575 

576 def MovePortItem(self, portItem: PortSignalInterfaceItem, index: int) -> None: 

577 """ 

578 Move a port to a position of the port clause. 

579 

580 The port stays a port of this language construct: only the order of the port clause changes. 

581 

582 :param portItem: The port to move. 

583 :param index: Position of the port after the move, from ``0`` (first) to :data:`PortCount` minus one 

584 (last). A negative index counts from the end, as for a :class:`list` index: ``-1`` moves the 

585 port to the end. 

586 :raises ValueError: If parameter 'portItem' is ``None``. 

587 :raises TypeError: If parameter 'portItem' is not of type 

588 :class:`~pyVHDLModel.Interface.PortSignalInterfaceItem`. 

589 :raises NotAChildError: If parameter 'portItem' is not a port of this language construct. 

590 :raises ValueError: If parameter 'index' is ``None``. 

591 :raises TypeError: If parameter 'index' is not of type :class:`int`. 

592 :raises ValueError: If parameter 'index' is out of range. 

593 

594 .. seealso:: 

595 

596 :meth:`InsertPortItem` 

597 |rarr| Insert a port at a position of the port clause. 

598 :meth:`RemovePortItem` 

599 |rarr| Remove a port from the port clause. 

600 """ 

601 from pyVHDLModel.Interface import PortSignalInterfaceItem 

602 

603 if portItem is None: 

604 raise ValueError("Parameter 'portItem' is None.") 

605 elif not isinstance(portItem, PortSignalInterfaceItem): 

606 ex = TypeError("Parameter 'portItem' is not of type 'PortSignalInterfaceItem'.") 

607 ex.add_note(f"Got type '{getFullyQualifiedName(portItem)}'.") 

608 raise ex 

609 

610 for currentIndex, item in enumerate(self._portItems): 

611 if item is portItem: 

612 break 

613 else: 

614 identifiers = ", ".join(identifiersOf(portItem)) 

615 raise NotAChildError( 

616 f"{getTypeName(portItem)} '{identifiers}' is not a port of this {getTypeName(self)}.", 

617 element=portItem, 

618 parent=self 

619 ) 

620 

621 if index is None: 621 ↛ 622line 621 didn't jump to line 622 because the condition on line 621 was never true

622 raise ValueError("Parameter 'index' is None.") 

623 elif not isinstance(index, int): 623 ↛ 624line 623 didn't jump to line 624 because the condition on line 623 was never true

624 ex = TypeError("Parameter 'index' is not of type 'int'.") 

625 ex.add_note(f"Got type '{getFullyQualifiedName(index)}'.") 

626 raise ex 

627 elif not (-len(self._portItems) <= index < len(self._portItems)): 

628 ex = ValueError(f"Parameter 'index' is out of range {-len(self._portItems)}..{len(self._portItems) - 1}.") 

629 ex.add_note(f"Got value '{index}'.") 

630 raise ex 

631 

632 newIndex = index if index >= 0 else len(self._portItems) + index 

633 del self._portItems[currentIndex] 

634 self._portItems.insert(newIndex, portItem) 

635 

636 @expects("_namespace") 

637 def _IndexPortItems(self) -> None: 

638 """Add the ports to the host's namespace.""" 

639 for item in self._portItems: 

640 for normalizedIdentifier in normalizedIdentifiersOf(item): 

641 self._namespace.AddElement(normalizedIdentifier, item) 

642 

643 

644@export 

645class WithParametersMixin( 

646 metaclass=ExtendedType, 

647 mixin=True, 

648 expects=("_AddToNamespace", "_RemoveFromNamespace", "_ClashingDeclarations") 

649): 

650 """ 

651 A mixin-class for language constructs with a parameter list. 

652 

653 The host class provides ``_AddToNamespace(element)`` and ``_RemoveFromNamespace(element)``, called when a parameter is 

654 inserted or removed: :class:`~pyVHDLModel.Regions.DeclarationRegionMixin` updates its namespace, a language construct 

655 without a namespace implements both as no-operations. 

656 

657 The host class also provides ``_ClashingDeclarations(item)``, called before a parameter is inserted: it returns the 

658 declarations sharing an identifier with the new item. 

659 

660 Indexing the parameters (``_IndexParameterItems()``) needs the host's namespace ``_namespace``; a host without one, 

661 e.g. a parameter group, can't index them. 

662 

663 .. seealso:: 

664 

665 * :class:`Subprogram <pyVHDLModel.Subprogram.Subprogram>` 

666 * :class:`Parameter group <pyVHDLModel.Interface.ParameterGroup>` 

667 """ 

668 _parameterItems: list[ParameterInterfaceItemMixin] #: List of all parameters, in declaration order. 

669 

670 def __init__( 

671 self, 

672 parameterItems: Nullable[Iterable[ParameterInterfaceItemMixin]] = None, 

673 ) -> None: 

674 """ 

675 Initializes a language construct with a parameter list. 

676 

677 :param parameterItems: List of all parameters, in declaration order. 

678 """ 

679 self._parameterItems = [] 

680 if parameterItems is not None: 

681 for item in parameterItems: 

682 self._parameterItems.append(item) 

683 item.Parent = self 

684 

685 @readonly 

686 def ParameterItems(self) -> list[ParameterInterfaceItemMixin]: 

687 """ 

688 Read-only property to access the parameter items (:attr:`_parameterItems`). 

689 

690 :returns: List of parameter items. 

691 """ 

692 return self._parameterItems 

693 

694 @readonly 

695 def ParameterCount(self) -> int: 

696 """ 

697 Read-only property to return the number of parameters in :attr:`_parameterItems`. 

698 

699 :returns: The parameter count. 

700 """ 

701 return len(self._parameterItems) 

702 

703 def AddParameterItem(self, parameterItem: ParameterInterfaceItemMixin) -> None: 

704 """ 

705 Add a parameter at the end of the parameter list. 

706 

707 The parameter's parent is set to this language construct, and its identifiers are added to the language construct's 

708 namespace, if it has one. 

709 

710 :param parameterItem: The parameter to add. 

711 :raises ValueError: If parameter 'parameterItem' is ``None``. 

712 :raises TypeError: If parameter 'parameterItem' is not of type 

713 :class:`~pyVHDLModel.Interface.ParameterInterfaceItemMixin`. 

714 :raises AlreadyInDesignError: If parameter 'parameterItem' already has a parent. 

715 :raises DuplicateParameterError: If an existing declaration of the language construct shares an identifier 

716 with the parameter. 

717 

718 .. seealso:: 

719 

720 :meth:`InsertParameterItem` 

721 |rarr| Insert a parameter at a position of the parameter list. 

722 :meth:`RemoveParameterItem` 

723 |rarr| Remove a parameter from the parameter list. 

724 """ 

725 self.InsertParameterItem(len(self._parameterItems), parameterItem) 

726 

727 def InsertParameterItem(self, index: int, parameterItem: ParameterInterfaceItemMixin) -> None: 

728 """ 

729 Insert a parameter at a position of the parameter list. 

730 

731 The parameter's parent is set to this language construct, and its identifiers are added to the language construct's 

732 namespace, if it has one. 

733 

734 :param index: Position, from ``0`` (first) to :data:`ParameterCount` (append). 

735 A negative index counts from the end, as for :meth:`list.insert`: ``-1`` inserts 

736 before the last parameter. 

737 :param parameterItem: The parameter to insert. 

738 :raises ValueError: If parameter 'index' is ``None``. 

739 :raises TypeError: If parameter 'index' is not of type :class:`int`. 

740 :raises ValueError: If parameter 'index' is out of range. 

741 :raises ValueError: If parameter 'parameterItem' is ``None``. 

742 :raises TypeError: If parameter 'parameterItem' is not of type 

743 :class:`~pyVHDLModel.Interface.ParameterInterfaceItemMixin`. 

744 :raises AlreadyInDesignError: If parameter 'parameterItem' already has a parent. 

745 :raises DuplicateParameterError: If an existing declaration of the language construct shares an identifier 

746 with the parameter. 

747 

748 .. seealso:: 

749 

750 :meth:`AddParameterItem` 

751 |rarr| Add a parameter at the end of the parameter list. 

752 :meth:`RemoveParameterItem` 

753 |rarr| Remove a parameter from the parameter list. 

754 :meth:`MoveParameterItem` 

755 |rarr| Move a parameter to a position of the parameter list. 

756 """ 

757 from pyVHDLModel.Interface import ParameterInterfaceItemMixin 

758 

759 if index is None: 759 ↛ 760line 759 didn't jump to line 760 because the condition on line 759 was never true

760 raise ValueError("Parameter 'index' is None.") 

761 elif not isinstance(index, int): 761 ↛ 762line 761 didn't jump to line 762 because the condition on line 761 was never true

762 ex = TypeError("Parameter 'index' is not of type 'int'.") 

763 ex.add_note(f"Got type '{getFullyQualifiedName(index)}'.") 

764 raise ex 

765 elif not (-len(self._parameterItems) <= index <= len(self._parameterItems)): 

766 ex = ValueError(f"Parameter 'index' is out of range {-len(self._parameterItems)}..{len(self._parameterItems)}.") 

767 ex.add_note(f"Got value '{index}'.") 

768 raise ex 

769 

770 if parameterItem is None: 

771 raise ValueError("Parameter 'parameterItem' is None.") 

772 elif not isinstance(parameterItem, ParameterInterfaceItemMixin): 

773 ex = TypeError("Parameter 'parameterItem' is not of type 'ParameterInterfaceItemMixin'.") 

774 ex.add_note(f"Got type '{getFullyQualifiedName(parameterItem)}'.") 

775 raise ex 

776 elif parameterItem._parent is not None: 

777 identifiers = ", ".join(identifiersOf(parameterItem)) 

778 ex = AlreadyInDesignError( 

779 f"{getTypeName(parameterItem)} '{identifiers}' already has a parent.", 

780 element=parameterItem, 

781 parent=parameterItem._parent 

782 ) 

783 ex.add_note(f"Remove it from its {getTypeName(parameterItem._parent)} first.") 

784 raise ex 

785 

786 if len(clashes := self._ClashingDeclarations(parameterItem)) > 0: 

787 existingIdentifiers = normalizedIdentifiersOf(clashes[0]) 

788 newIdentifiers = zip(identifiersOf(parameterItem), normalizedIdentifiersOf(parameterItem)) 

789 identifier = next(name for name, normalizedName in newIdentifiers if normalizedName in existingIdentifiers) 

790 ex = DuplicateParameterError( 

791 f"{getTypeName(clashes[0]._parent)} already declares '{identifier}'.", 

792 identifier=identifier, 

793 parent=self, 

794 element=parameterItem, 

795 existing=clashes[0] 

796 ) 

797 ex.add_note(f"Existing declaration is of type '{getFullyQualifiedName(clashes[0])}'.") 

798 raise ex 

799 

800 self._parameterItems.insert(index, parameterItem) 

801 parameterItem.Parent = self 

802 self._AddToNamespace(parameterItem) 

803 

804 def RemoveParameterItem(self, parameterItem: ParameterInterfaceItemMixin) -> None: 

805 """ 

806 Remove a parameter from the parameter list. 

807 

808 The parameter is detached: its parent is cleared and its identifiers are removed from the language construct's 

809 namespace, if it has one. The parameter itself is unchanged and can be added to another language construct, or 

810 inserted at another position. 

811 

812 :param parameterItem: The parameter to remove. 

813 :raises ValueError: If parameter 'parameterItem' is ``None``. 

814 :raises TypeError: If parameter 'parameterItem' is not of type 

815 :class:`~pyVHDLModel.Interface.ParameterInterfaceItemMixin`. 

816 :raises NotAChildError: If parameter 'parameterItem' is not a parameter of this language construct. 

817 :raises NotInNamespaceError: If the namespace is indexed, and an identifier of the parameter isn't declared in it. 

818 :raises NotInNamespaceError: If the namespace is indexed, and an identifier of the parameter refers to another 

819 element, e.g. a duplicate declaration. 

820 

821 .. seealso:: 

822 

823 :meth:`AddParameterItem` 

824 |rarr| Add a parameter at the end of the parameter list. 

825 :meth:`InsertParameterItem` 

826 |rarr| Insert a parameter at a position of the parameter list. 

827 :meth:`MoveParameterItem` 

828 |rarr| Move a parameter to a position of the parameter list. 

829 """ 

830 from pyVHDLModel.Interface import ParameterInterfaceItemMixin 

831 

832 if parameterItem is None: 

833 raise ValueError("Parameter 'parameterItem' is None.") 

834 elif not isinstance(parameterItem, ParameterInterfaceItemMixin): 

835 ex = TypeError("Parameter 'parameterItem' is not of type 'ParameterInterfaceItemMixin'.") 

836 ex.add_note(f"Got type '{getFullyQualifiedName(parameterItem)}'.") 

837 raise ex 

838 

839 for index, item in enumerate(self._parameterItems): 

840 if item is parameterItem: 

841 break 

842 else: 

843 identifiers = ", ".join(identifiersOf(parameterItem)) 

844 raise NotAChildError( 

845 f"{getTypeName(parameterItem)} '{identifiers}' is not a parameter of this {getTypeName(self)}.", 

846 element=parameterItem, 

847 parent=self 

848 ) 

849 

850 self._RemoveFromNamespace(parameterItem) 

851 del self._parameterItems[index] 

852 parameterItem._parent = None 

853 

854 def MoveParameterItem(self, parameterItem: ParameterInterfaceItemMixin, index: int) -> None: 

855 """ 

856 Move a parameter to a position of the parameter list. 

857 

858 The parameter stays a parameter of this language construct: only the order of the parameter list changes. 

859 

860 :param parameterItem: The parameter to move. 

861 :param index: Position of the parameter after the move, from ``0`` (first) to :data:`ParameterCount` 

862 minus one (last). A negative index counts from the end, as for a :class:`list` index: 

863 ``-1`` moves the parameter to the end. 

864 :raises ValueError: If parameter 'parameterItem' is ``None``. 

865 :raises TypeError: If parameter 'parameterItem' is not of type 

866 :class:`~pyVHDLModel.Interface.ParameterInterfaceItemMixin`. 

867 :raises NotAChildError: If parameter 'parameterItem' is not a parameter of this language construct. 

868 :raises ValueError: If parameter 'index' is ``None``. 

869 :raises TypeError: If parameter 'index' is not of type :class:`int`. 

870 :raises ValueError: If parameter 'index' is out of range. 

871 

872 .. seealso:: 

873 

874 :meth:`InsertParameterItem` 

875 |rarr| Insert a parameter at a position of the parameter list. 

876 :meth:`RemoveParameterItem` 

877 |rarr| Remove a parameter from the parameter list. 

878 """ 

879 from pyVHDLModel.Interface import ParameterInterfaceItemMixin 

880 

881 if parameterItem is None: 

882 raise ValueError("Parameter 'parameterItem' is None.") 

883 elif not isinstance(parameterItem, ParameterInterfaceItemMixin): 

884 ex = TypeError("Parameter 'parameterItem' is not of type 'ParameterInterfaceItemMixin'.") 

885 ex.add_note(f"Got type '{getFullyQualifiedName(parameterItem)}'.") 

886 raise ex 

887 

888 for currentIndex, item in enumerate(self._parameterItems): 

889 if item is parameterItem: 

890 break 

891 else: 

892 identifiers = ", ".join(identifiersOf(parameterItem)) 

893 raise NotAChildError( 

894 f"{getTypeName(parameterItem)} '{identifiers}' is not a parameter of this {getTypeName(self)}.", 

895 element=parameterItem, 

896 parent=self 

897 ) 

898 

899 if index is None: 899 ↛ 900line 899 didn't jump to line 900 because the condition on line 899 was never true

900 raise ValueError("Parameter 'index' is None.") 

901 elif not isinstance(index, int): 901 ↛ 902line 901 didn't jump to line 902 because the condition on line 901 was never true

902 ex = TypeError("Parameter 'index' is not of type 'int'.") 

903 ex.add_note(f"Got type '{getFullyQualifiedName(index)}'.") 

904 raise ex 

905 elif not (-len(self._parameterItems) <= index < len(self._parameterItems)): 

906 ex = ValueError( 

907 f"Parameter 'index' is out of range {-len(self._parameterItems)}..{len(self._parameterItems) - 1}." 

908 ) 

909 ex.add_note(f"Got value '{index}'.") 

910 raise ex 

911 

912 newIndex = index if index >= 0 else len(self._parameterItems) + index 

913 del self._parameterItems[currentIndex] 

914 self._parameterItems.insert(newIndex, parameterItem) 

915 

916 @expects("_namespace") 

917 def _IndexParameterItems(self) -> None: 

918 """Add the parameters to the host's namespace.""" 

919 for item in self._parameterItems: 

920 for normalizedIdentifier in normalizedIdentifiersOf(item): 

921 self._namespace.AddElement(normalizedIdentifier, item) 

922 

923 

924@export 

925class DeclarationRegionMixin( 

926 metaclass=ExtendedType, 

927 mixin=True, 

928 expects=( 

929 "_namespace", 

930 "_functions", 

931 "_procedures", 

932 "_declaredItems", 

933 "_Declarations", 

934 "_IndexDeclaredItem", 

935 "_UnindexDeclaredItem", 

936 "IndexDeclaredItems", 

937 "_Labels" 

938 ) 

939): 

940 """ 

941 A base-class for the concurrent, sequential and protected type declaration region mixins. 

942 

943 It carries what all regions share: adding interface items to the region's namespace, the mutators for declared items 

944 (e.g. :meth:`AddDeclaredItem`), and the hooks for declared items no region handles itself. 

945 

946 The derived region mixin-class provides the namespace ``_namespace``, the declared items ``_declaredItems`` and the 

947 lookup tables ``_functions`` and ``_procedures``; it or the host class provides the methods ``_Declarations()``, 

948 ``_IndexDeclaredItem(item)``, ``_UnindexDeclaredItem(item)`` and ``IndexDeclaredItems()``. 

949 

950 An interface item shares the declarative region of the declarative part beside it - VHDL rejects 

951 ``port (g : in bit)`` beside ``generic (g : integer)``, ``signal x`` beside ``port (x : in bit)``, and a 

952 subprogram variable named like one of its parameters, all as "identifier already used for a 

953 declaration". So they belong in the region's *own* namespace, not a separate one. 

954 

955 Which of the three a region has is known statically by the class that declares them, so each derived 

956 class calls the ones it needs from its own :meth:`IndexDeclaredItems` before delegating upwards. 

957 Interface items are added to the namespace only - ``GenericItems``/``PortItems``/``ParameterItems`` 

958 already expose them as ordered lists, so no extra lookup table is needed. 

959 

960 .. seealso:: 

961 

962 * :class:`Concurrent declaration region mixin <pyVHDLModel.Regions.ConcurrentDeclarationRegionMixin>` 

963 * :class:`Sequential declaration region mixin <pyVHDLModel.Regions.SequentialDeclarationRegionMixin>` 

964 * :class:`Protected type declaration region mixin <pyVHDLModel.Regions.ProtectedTypeDeclarationRegionMixin>` 

965 

966 A statement label is declared implicitly in the declarative region enclosing the statement, so the host provides the 

967 labeled statements of the region by ``_Labels()``: a host with statements gets it from its statements mixin 

968 (e.g. :class:`~pyVHDLModel.Concurrent.ConcurrentStatementsMixin`), a host without statements from 

969 :class:`WithoutStatementsMixin`. 

970 """ 

971 _DECLARED_ITEM_TYPES: ClassVar[tuple[type, ...]] #: Accepted classes of declared items (:mod:`pyVHDLModel`). 

972 _areDeclaredItemsIndexed: bool #: ``True`` if :meth:`IndexDeclaredItems` filled the namespace. 

973 

974 def __init__(self) -> None: 

975 """ 

976 Initializes a declaration region, which isn't indexed yet. 

977 """ 

978 self._areDeclaredItemsIndexed = False 

979 

980 def _AddToNamespace(self, element: NamedEntityMixin | MultipleNamedEntityMixin) -> None: 

981 """ 

982 Add an element's identifiers to this region's namespace. 

983 

984 Nothing is added to a region not indexed yet: :meth:`IndexDeclaredItems` adds the element later. A generic 

985 subprogram is an overload (:meth:`_AddOverloadToNamespace`). 

986 

987 :param element: The named element to add, e.g. an interface item. 

988 """ 

989 from pyVHDLModel.Subprogram import Function, Procedure 

990 

991 if not self._areDeclaredItemsIndexed: 

992 return 

993 elif isinstance(element, (Function, Procedure)): 

994 self._AddOverloadToNamespace(element) 

995 return 

996 

997 for normalizedIdentifier in normalizedIdentifiersOf(element): 

998 self._namespace.AddElement(normalizedIdentifier, element) 

999 

1000 def _RemoveFromNamespace(self, element: NamedEntityMixin | MultipleNamedEntityMixin) -> None: 

1001 """ 

1002 Remove an element's identifiers from this region's namespace. 

1003 

1004 Nothing is removed from a region not indexed yet, as its namespace doesn't contain the element. A generic 

1005 subprogram is an overload (:meth:`_RemoveOverloadFromNamespace`). 

1006 

1007 :param element: The named element to remove, e.g. an interface item. 

1008 :raises NotInNamespaceError: If the region is indexed, and an identifier of the element isn't declared in the 

1009 namespace. 

1010 :raises NotInNamespaceError: If the region is indexed, and an identifier of the element refers to another element, 

1011 e.g. a duplicate declaration. 

1012 """ 

1013 from pyVHDLModel.Subprogram import Function, Procedure 

1014 

1015 if not self._areDeclaredItemsIndexed: 

1016 return 

1017 elif isinstance(element, (Function, Procedure)): 

1018 self._RemoveOverloadFromNamespace(element) 

1019 return 

1020 

1021 for normalizedIdentifier in normalizedIdentifiersOf(element): 

1022 self._namespace.RemoveElement(normalizedIdentifier, element) 

1023 

1024 def _IndexOtherDeclaredItem(self, item: ModelEntity) -> None: 

1025 """ 

1026 Hook for declared items the region doesn't handle itself. Derived classes may override it. 

1027 

1028 :param item: The declared item. 

1029 """ 

1030 

1031 def _UnindexOtherDeclaredItem(self, item: ModelEntity) -> None: 

1032 """ 

1033 Hook to remove declared items the region doesn't handle itself. Derived classes may override it. 

1034 

1035 :param item: The declared item. 

1036 """ 

1037 

1038 def _RemoveDeclaredItemsFromNamespace(self) -> None: 

1039 """ 

1040 Remove all declared items of this region from its namespace. 

1041 

1042 Interface items of this region and items imported from elsewhere (e.g. by a use clause) are kept. 

1043 """ 

1044 from pyVHDLModel.Interface import InterfaceItemMixin 

1045 

1046 for normalizedIdentifier, element in list(self._namespace.Elements().items()): 

1047 if not isinstance(element, InterfaceItemMixin) and element._parent is self: 

1048 self._namespace.RemoveElement(normalizedIdentifier, element) 

1049 

1050 def _RemoveDeclaredItemEntries( 

1051 self, 

1052 lookupTable: dict[str, Any], 

1053 item: NamedEntityMixin | MultipleNamedEntityMixin 

1054 ) -> None: 

1055 """ 

1056 Remove a declared item's entries from a lookup table and from the namespace. 

1057 

1058 Every identifier is checked before anything is removed. 

1059 

1060 :param lookupTable: The lookup table matching the declared item's kind. 

1061 :param item: The declared item. 

1062 :raises NotInNamespaceError: If an identifier of the declared item isn't declared in the namespace. 

1063 :raises NotInNamespaceError: If an identifier of the declared item refers to another element, e.g. a duplicate 

1064 declaration. 

1065 """ 

1066 normalizedIdentifiers = normalizedIdentifiersOf(item) 

1067 elements = self._namespace.Elements() 

1068 for normalizedIdentifier in normalizedIdentifiers: 

1069 if elements.get(normalizedIdentifier) is not item: 

1070 # raises NotInNamespaceError, naming the reason 

1071 self._namespace.RemoveElement(normalizedIdentifier, item) 

1072 

1073 for normalizedIdentifier in normalizedIdentifiers: 

1074 del lookupTable[normalizedIdentifier] 

1075 self._namespace.RemoveElement(normalizedIdentifier, item) 

1076 

1077 def _IndexOverload(self, item: NamedEntityMixin | MultipleNamedEntityMixin) -> None: 

1078 """ 

1079 Add an inserted function or procedure to its lookup table and to the namespace. 

1080 

1081 The list of overloads follows the declaration order. The namespace is updated by :meth:`_AddOverloadToNamespace`. 

1082 

1083 :param item: The inserted function or procedure. 

1084 """ 

1085 from pyVHDLModel.Subprogram import Function, Procedure 

1086 

1087 kind = Function if isinstance(item, Function) else Procedure 

1088 lookupTable = self._functions if kind is Function else self._procedures 

1089 normalizedIdentifier = item._normalizedIdentifier 

1090 lookupTable[normalizedIdentifier] = [ 

1091 declaration for declaration in self._declaredItems 

1092 if isinstance(declaration, kind) and declaration._normalizedIdentifier == normalizedIdentifier 

1093 ] 

1094 

1095 self._AddOverloadToNamespace(item) 

1096 

1097 def _UnindexOverload(self, item: NamedEntityMixin | MultipleNamedEntityMixin) -> None: 

1098 """ 

1099 Remove a function or procedure from its lookup table and from the namespace. 

1100 

1101 The namespace is updated first, by :meth:`_RemoveOverloadFromNamespace`. 

1102 

1103 :param item: The function or procedure to remove, still a declared item of this region. 

1104 :raises NotInNamespaceError: If the identifier isn't declared in the namespace. 

1105 :raises NotInNamespaceError: If the identifier refers to an element, which is no overload, e.g. a duplicate 

1106 declaration. 

1107 """ 

1108 from pyVHDLModel.Subprogram import Function 

1109 

1110 self._RemoveOverloadFromNamespace(item) 

1111 

1112 normalizedIdentifier = item._normalizedIdentifier 

1113 lookupTable = self._functions if isinstance(item, Function) else self._procedures 

1114 overloads = lookupTable[normalizedIdentifier] 

1115 overloads.remove(item) 

1116 if len(overloads) == 0: 

1117 del lookupTable[normalizedIdentifier] 

1118 

1119 def _AddOverloadToNamespace(self, item: NamedEntityMixin | MultipleNamedEntityMixin) -> None: 

1120 """ 

1121 Add a function or procedure to the namespace, if it's the overload declared last. 

1122 

1123 The namespace keeps one entry per identifier: the overload declared last, as after :meth:`IndexDeclaredItems`, which 

1124 adds interface items first, then declared items (:meth:`_Declarations`). 

1125 

1126 :param item: The function or procedure, already an interface item or declared item of this region. 

1127 """ 

1128 from pyVHDLModel.Subprogram import Function, Procedure 

1129 

1130 normalizedIdentifier = item._normalizedIdentifier 

1131 overloads = [ 

1132 declaration for declaration in self._Declarations() 

1133 if isinstance(declaration, (Function, Procedure)) and declaration._normalizedIdentifier == normalizedIdentifier 

1134 ] 

1135 if overloads[-1] is item: 

1136 self._namespace.AddElement(normalizedIdentifier, item, overloadable=True) 

1137 

1138 def _RemoveOverloadFromNamespace(self, item: NamedEntityMixin | MultipleNamedEntityMixin) -> None: 

1139 """ 

1140 Remove a function or procedure from the namespace. 

1141 

1142 Removing the overload the namespace refers to, the namespace refers to the remaining overload declared last 

1143 (:meth:`_Declarations`), or the identifier is removed, if no overload remains. Removing another overload leaves the 

1144 namespace unchanged. The namespace is checked before it's changed. 

1145 

1146 :param item: The function or procedure, still an interface item or declared item of this region. 

1147 :raises NotInNamespaceError: If the identifier isn't declared in the namespace. 

1148 :raises NotInNamespaceError: If the identifier refers to an element, which is no overload, e.g. a duplicate 

1149 declaration. 

1150 """ 

1151 from pyVHDLModel.Subprogram import Function, Procedure 

1152 

1153 normalizedIdentifier = item._normalizedIdentifier 

1154 entry = self._namespace.Elements().get(normalizedIdentifier) 

1155 if entry is item: 

1156 remaining = [ 

1157 declaration for declaration in self._Declarations() 

1158 if declaration is not item 

1159 and isinstance(declaration, (Function, Procedure)) 

1160 and declaration._normalizedIdentifier == normalizedIdentifier 

1161 ] 

1162 if len(remaining) == 0: 

1163 self._namespace.RemoveElement(normalizedIdentifier, item) 

1164 else: 

1165 self._namespace.AddElement(normalizedIdentifier, remaining[-1], overloadable=True) 

1166 elif not isinstance(entry, (Function, Procedure)): 

1167 # raises NotInNamespaceError, naming the reason 

1168 self._namespace.RemoveElement(normalizedIdentifier, item) 

1169 

1170 def _SharedRegions(self) -> tuple[DeclarationRegionMixin, ...]: 

1171 """ 

1172 Return the other language constructs forming one declarative region with this one. 

1173 

1174 An entity and each of its architectures form one declarative region, as do a package and its body. Derived classes 

1175 override it. 

1176 

1177 :returns: Tuple of declarative regions. 

1178 """ 

1179 return () 

1180 

1181 def _MayShareIdentifier(self, item: ModelEntity, existing: ModelEntity) -> bool: 

1182 """ 

1183 Check if two declarations may share an identifier in one declarative region. 

1184 

1185 Subprograms are overloadable. A protected type body completes its protected type, a full type completes its 

1186 incomplete type, and a constant completes its deferred constant. 

1187 

1188 :param item: The new declaration. 

1189 :param existing: The existing declaration. 

1190 :returns: ``True``, if both declarations may share an identifier. 

1191 """ 

1192 from pyVHDLModel.Interface import InterfaceItemMixin 

1193 from pyVHDLModel.Subprogram import Function, Procedure 

1194 from pyVHDLModel.Type import FullType, AnonymousType, ProtectedType, ProtectedTypeBody 

1195 

1196 return ( 

1197 (isinstance(item, (Function, Procedure)) and isinstance(existing, (Function, Procedure))) 

1198 or (isinstance(item, ProtectedTypeBody) and isinstance(existing, ProtectedType)) 

1199 or (isinstance(item, ProtectedType) and isinstance(existing, ProtectedTypeBody)) 

1200 or (isinstance(item, FullType) and isinstance(existing, AnonymousType)) 

1201 or (isinstance(item, AnonymousType) and isinstance(existing, FullType)) 

1202 or ( 

1203 isinstance(item, Constant) and not isinstance(item, InterfaceItemMixin) 

1204 and isinstance(existing, DeferredConstant) 

1205 ) 

1206 or ( 

1207 isinstance(item, DeferredConstant) 

1208 and isinstance(existing, Constant) and not isinstance(existing, InterfaceItemMixin) 

1209 ) 

1210 ) 

1211 

1212 def _ClashingDeclarations(self, item: ModelEntity) -> tuple[ModelEntity, ...]: 

1213 """ 

1214 Return the declarations sharing an identifier with the given item, which may not share it. 

1215 

1216 The declarations (:meth:`_Declarations`) and the labeled statements (``_Labels()``) of this declarative region and 

1217 of the language constructs sharing it (:meth:`_SharedRegions`) are searched: a statement label is declared 

1218 implicitly. Items imported into the namespace (e.g. by a use clause) are no declarations. 

1219 

1220 :param item: The new declaration, or the new labeled statement. 

1221 :returns: Tuple of clashing declarations, interface items first, then declared items in declaration order, then 

1222 labeled statements. 

1223 """ 

1224 if len(names := set(normalizedDeclaredNamesOf(item))) == 0: 

1225 return () 

1226 

1227 return tuple( 

1228 declaration 

1229 for region in (self, *self._SharedRegions()) 

1230 for declaration in (*region._Declarations(), *region._Labels()) 

1231 if ( 

1232 declaration is not item 

1233 and not names.isdisjoint(normalizedDeclaredNamesOf(declaration)) 

1234 and not self._MayShareIdentifier(item, declaration) 

1235 ) 

1236 ) 

1237 

1238 def _DeclaredItemsSharingIdentifiers(self, declaredItem: ModelEntity) -> list[ModelEntity]: 

1239 """ 

1240 Return the other declared items of this region sharing an identifier with the given declared item. 

1241 

1242 :param declaredItem: The declared item. 

1243 :returns: List of declared items, in declaration order. 

1244 """ 

1245 if not isinstance(declaredItem, (NamedEntityMixin, MultipleNamedEntityMixin)): 

1246 return [] 

1247 

1248 identifiers = set(normalizedIdentifiersOf(declaredItem)) 

1249 return [ 

1250 item for item in self._declaredItems 

1251 if item is not declaredItem 

1252 and isinstance(item, (NamedEntityMixin, MultipleNamedEntityMixin)) 

1253 and not identifiers.isdisjoint(normalizedIdentifiersOf(item)) 

1254 ] 

1255 

1256 def AddDeclaredItem(self, declaredItem: ModelEntity) -> None: 

1257 """ 

1258 Add a declared item at the end of the declarative region. 

1259 

1260 The declared item's parent is set to this declarative region. If the region is indexed, its identifiers are added 

1261 to the region's namespace and to the lookup table matching its kind. 

1262 

1263 :param declaredItem: The declared item to add. 

1264 :raises ValueError: If parameter 'declaredItem' is ``None``. 

1265 :raises TypeError: If parameter 'declaredItem' is not a declaration allowed in this declarative 

1266 region. 

1267 :raises AlreadyInDesignError: If parameter 'declaredItem' already has a parent. 

1268 :raises DuplicateDeclarationError: If an existing declaration of the declarative region shares an 

1269 identifier with the declared item. 

1270 

1271 .. seealso:: 

1272 

1273 :meth:`InsertDeclaredItem` 

1274 |rarr| Insert a declared item at a position of the declarative region. 

1275 :meth:`RemoveDeclaredItem` 

1276 |rarr| Remove a declared item from the declarative region. 

1277 """ 

1278 self.InsertDeclaredItem(len(self._declaredItems), declaredItem) 

1279 

1280 def InsertDeclaredItem(self, index: int, declaredItem: ModelEntity) -> None: 

1281 """ 

1282 Insert a declared item at a position of the declarative region. 

1283 

1284 The declared item's parent is set to this declarative region. If the region is indexed, its identifiers are added 

1285 to the region's namespace and to the lookup table matching its kind. 

1286 

1287 An identifier is declared once per declarative region: interface items and declared items count, including those of 

1288 the language construct sharing the declarative region (an entity and its architecture, a package and its body). 

1289 Subprograms are overloadable: a subprogram may share its identifier with other subprograms. A protected type body 

1290 may share its identifier with its protected type, a full type with its incomplete type declaration, and a constant 

1291 with its deferred constant. Items imported into the namespace (e.g. by a use clause) are hidden. 

1292 

1293 :param index: Position, from ``0`` (first) to the number of declared items (append). 

1294 A negative index counts from the end, as for :meth:`list.insert`: ``-1`` 

1295 inserts before the last declared item. 

1296 :param declaredItem: The declared item to insert. 

1297 :raises ValueError: If parameter 'index' is ``None``. 

1298 :raises TypeError: If parameter 'index' is not of type :class:`int`. 

1299 :raises ValueError: If parameter 'index' is out of range. 

1300 :raises ValueError: If parameter 'declaredItem' is ``None``. 

1301 :raises TypeError: If parameter 'declaredItem' is not a declaration allowed in this declarative 

1302 region. 

1303 :raises AlreadyInDesignError: If parameter 'declaredItem' already has a parent. 

1304 :raises DuplicateDeclarationError: If an existing declaration of the declarative region shares an 

1305 identifier with the declared item. 

1306 

1307 .. seealso:: 

1308 

1309 :meth:`AddDeclaredItem` 

1310 |rarr| Add a declared item at the end of the declarative region. 

1311 :meth:`InsertDeclaredItemBefore` 

1312 |rarr| Insert a declared item before another declared item. 

1313 :meth:`InsertDeclaredItemAfter` 

1314 |rarr| Insert a declared item after another declared item. 

1315 :meth:`RemoveDeclaredItem` 

1316 |rarr| Remove a declared item from the declarative region. 

1317 :meth:`MoveDeclaredItem` 

1318 |rarr| Move a declared item to a position of the declarative region. 

1319 """ 

1320 from pyVHDLModel.DesignUnit import Component 

1321 from pyVHDLModel.Interface import InterfaceItemMixin 

1322 from pyVHDLModel.Subprogram import Function, Procedure 

1323 from pyVHDLModel.Type import BaseType, Subtype 

1324 

1325 if index is None: 

1326 raise ValueError("Parameter 'index' is None.") 

1327 elif not isinstance(index, int): 

1328 ex = TypeError("Parameter 'index' is not of type 'int'.") 

1329 ex.add_note(f"Got type '{getFullyQualifiedName(index)}'.") 

1330 raise ex 

1331 elif not (-len(self._declaredItems) <= index <= len(self._declaredItems)): 

1332 ex = ValueError(f"Parameter 'index' is out of range {-len(self._declaredItems)}..{len(self._declaredItems)}.") 

1333 ex.add_note(f"Got value '{index}'.") 

1334 raise ex 

1335 

1336 if declaredItem is None: 

1337 raise ValueError("Parameter 'declaredItem' is None.") 

1338 elif not isinstance(declaredItem, self._DECLARED_ITEM_TYPES) or isinstance(declaredItem, InterfaceItemMixin): 

1339 ex = TypeError(f"Parameter 'declaredItem' is not a declaration allowed in {getTypeName(self)}.") 

1340 ex.add_note(f"Got type '{getFullyQualifiedName(declaredItem)}'.") 

1341 raise ex 

1342 elif declaredItem._parent is not None: 

1343 identifiers = "" 

1344 if isinstance(declaredItem, (NamedEntityMixin, MultipleNamedEntityMixin)): 

1345 identifiers = f" '{', '.join(identifiersOf(declaredItem))}'" 

1346 ex = AlreadyInDesignError( 

1347 f"{getTypeName(declaredItem)}{identifiers} already has a parent.", 

1348 element=declaredItem, 

1349 parent=declaredItem._parent 

1350 ) 

1351 ex.add_note(f"Remove it from its {getTypeName(declaredItem._parent)} first.") 

1352 raise ex 

1353 

1354 if len(clashes := self._ClashingDeclarations(declaredItem)) > 0: 

1355 existingIdentifiers = normalizedDeclaredNamesOf(clashes[0]) 

1356 newIdentifiers = zip(identifiersOf(declaredItem), normalizedIdentifiersOf(declaredItem)) 

1357 identifier = next(name for name, normalizedName in newIdentifiers if normalizedName in existingIdentifiers) 

1358 

1359 if isinstance(declaredItem, Subtype): 

1360 errorClass = DuplicateSubtypeError 

1361 elif isinstance(declaredItem, BaseType): 

1362 errorClass = DuplicateTypeError 

1363 elif isinstance(declaredItem, (Function, Procedure)): 

1364 errorClass = DuplicateSubprogramError 

1365 elif isinstance(declaredItem, BaseConstant): 

1366 errorClass = DuplicateConstantError 

1367 elif isinstance(declaredItem, Signal): 

1368 errorClass = DuplicateSignalError 

1369 elif isinstance(declaredItem, (Variable, SharedVariable)): 

1370 errorClass = DuplicateVariableError 

1371 elif isinstance(declaredItem, File): 

1372 errorClass = DuplicateFileError 

1373 elif isinstance(declaredItem, Component): 

1374 errorClass = DuplicateComponentError 

1375 else: 

1376 errorClass = DuplicateDeclarationError 

1377 region = clashes[0]._parent 

1378 while not isinstance(region, DeclarationRegionMixin): 

1379 region = region._parent 

1380 ex = errorClass( 

1381 f"{getTypeName(region)} already declares '{identifier}'.", 

1382 identifier=identifier, 

1383 namespace=self._namespace, 

1384 element=declaredItem, 

1385 existing=clashes[0] 

1386 ) 

1387 ex.add_note(f"Existing declaration is of type '{getFullyQualifiedName(clashes[0])}'.") 

1388 raise ex 

1389 

1390 sharingItems = self._DeclaredItemsSharingIdentifiers(declaredItem) 

1391 

1392 self._declaredItems.insert(index, declaredItem) 

1393 declaredItem.Parent = self 

1394 if self._areDeclaredItemsIndexed: 

1395 if isinstance(declaredItem, (Function, Procedure)): 

1396 self._IndexOverload(declaredItem) 

1397 else: 

1398 self._IndexDeclaredItem(declaredItem) 

1399 

1400 def InsertDeclaredItemBefore(self, declaredItem: ModelEntity, before: ModelEntity) -> None: 

1401 """ 

1402 Insert a declared item before another declared item. 

1403 

1404 The declared item's parent is set to this declarative region. If the region is indexed, its identifiers are added 

1405 to the region's namespace and to the lookup table matching its kind. 

1406 

1407 :param declaredItem: The declared item to insert. 

1408 :param before: The declared item of this declarative region to insert before. 

1409 :raises ValueError: If parameter 'before' is ``None``. 

1410 :raises TypeError: If parameter 'before' is not of type :class:`~pyVHDLModel.Base.ModelEntity`. 

1411 :raises NotAChildError: If parameter 'before' is not a declared item of this declarative region. 

1412 :raises ValueError: If parameter 'declaredItem' is ``None``. 

1413 :raises TypeError: If parameter 'declaredItem' is not a declaration allowed in this declarative 

1414 region. 

1415 :raises AlreadyInDesignError: If parameter 'declaredItem' already has a parent. 

1416 :raises DuplicateDeclarationError: If an existing declaration of the declarative region shares an 

1417 identifier with the declared item. 

1418 

1419 .. seealso:: 

1420 

1421 :meth:`InsertDeclaredItem` 

1422 |rarr| Insert a declared item at a position of the declarative region. 

1423 :meth:`InsertDeclaredItemAfter` 

1424 |rarr| Insert a declared item after another declared item. 

1425 """ 

1426 if before is None: 

1427 raise ValueError("Parameter 'before' is None.") 

1428 elif not isinstance(before, ModelEntity): 

1429 ex = TypeError("Parameter 'before' is not of type 'ModelEntity'.") 

1430 ex.add_note(f"Got type '{getFullyQualifiedName(before)}'.") 

1431 raise ex 

1432 

1433 for index, item in enumerate(self._declaredItems): 

1434 if item is before: 

1435 break 

1436 else: 

1437 identifiers = "" 

1438 if isinstance(before, (NamedEntityMixin, MultipleNamedEntityMixin)): 1438 ↛ 1440line 1438 didn't jump to line 1440 because the condition on line 1438 was always true

1439 identifiers = f" '{', '.join(identifiersOf(before))}'" 

1440 raise NotAChildError( 

1441 f"{getTypeName(before)}{identifiers} is not a declared item of this {getTypeName(self)}.", 

1442 element=before, 

1443 parent=self 

1444 ) 

1445 

1446 self.InsertDeclaredItem(index, declaredItem) 

1447 

1448 def InsertDeclaredItemAfter(self, declaredItem: ModelEntity, after: ModelEntity) -> None: 

1449 """ 

1450 Insert a declared item after another declared item. 

1451 

1452 The declared item's parent is set to this declarative region. If the region is indexed, its identifiers are added 

1453 to the region's namespace and to the lookup table matching its kind. 

1454 

1455 :param declaredItem: The declared item to insert. 

1456 :param after: The declared item of this declarative region to insert after. 

1457 :raises ValueError: If parameter 'after' is ``None``. 

1458 :raises TypeError: If parameter 'after' is not of type :class:`~pyVHDLModel.Base.ModelEntity`. 

1459 :raises NotAChildError: If parameter 'after' is not a declared item of this declarative region. 

1460 :raises ValueError: If parameter 'declaredItem' is ``None``. 

1461 :raises TypeError: If parameter 'declaredItem' is not a declaration allowed in this declarative 

1462 region. 

1463 :raises AlreadyInDesignError: If parameter 'declaredItem' already has a parent. 

1464 :raises DuplicateDeclarationError: If an existing declaration of the declarative region shares an 

1465 identifier with the declared item. 

1466 

1467 .. seealso:: 

1468 

1469 :meth:`InsertDeclaredItem` 

1470 |rarr| Insert a declared item at a position of the declarative region. 

1471 :meth:`InsertDeclaredItemBefore` 

1472 |rarr| Insert a declared item before another declared item. 

1473 """ 

1474 if after is None: 

1475 raise ValueError("Parameter 'after' is None.") 

1476 elif not isinstance(after, ModelEntity): 

1477 ex = TypeError("Parameter 'after' is not of type 'ModelEntity'.") 

1478 ex.add_note(f"Got type '{getFullyQualifiedName(after)}'.") 

1479 raise ex 

1480 

1481 for index, item in enumerate(self._declaredItems): 

1482 if item is after: 

1483 break 

1484 else: 

1485 identifiers = "" 

1486 if isinstance(after, (NamedEntityMixin, MultipleNamedEntityMixin)): 1486 ↛ 1487line 1486 didn't jump to line 1487 because the condition on line 1486 was never true

1487 identifiers = f" '{', '.join(identifiersOf(after))}'" 

1488 raise NotAChildError( 

1489 f"{getTypeName(after)}{identifiers} is not a declared item of this {getTypeName(self)}.", 

1490 element=after, 

1491 parent=self 

1492 ) 

1493 

1494 self.InsertDeclaredItem(index + 1, declaredItem) 

1495 

1496 def RemoveDeclaredItem(self, declaredItem: ModelEntity) -> None: 

1497 """ 

1498 Remove a declared item from the declarative region. 

1499 

1500 The declared item is detached: its parent is cleared. If the region is indexed, the declared item's identifiers are 

1501 removed from the region's namespace and from the lookup table matching its kind first; they are checked before 

1502 anything is changed. The declared item itself is unchanged and can be added to another declarative region, or 

1503 inserted at another position. 

1504 

1505 The namespace refers to the overload declared last: removing it, the namespace refers to the remaining overload 

1506 declared last. If another declaration shares an identifier with the declared item (e.g. a duplicate declaration 

1507 read by a parser), the region is indexed again (:meth:`IndexDeclaredItems`) after the removal. 

1508 

1509 :param declaredItem: The declared item to remove. 

1510 :raises ValueError: If parameter 'declaredItem' is ``None``. 

1511 :raises TypeError: If parameter 'declaredItem' is not of type :class:`~pyVHDLModel.Base.ModelEntity`. 

1512 :raises NotAChildError: If parameter 'declaredItem' is not a declared item of this declarative region. 

1513 :raises NotInNamespaceError: If the region is indexed, and an identifier of the declared item isn't declared in 

1514 its namespace. 

1515 :raises NotInNamespaceError: If the region is indexed, and an identifier of the declared item refers to another 

1516 declaration, e.g. a duplicate declaration. 

1517 

1518 .. seealso:: 

1519 

1520 :meth:`AddDeclaredItem` 

1521 |rarr| Add a declared item at the end of the declarative region. 

1522 :meth:`InsertDeclaredItem` 

1523 |rarr| Insert a declared item at a position of the declarative region. 

1524 :meth:`MoveDeclaredItem` 

1525 |rarr| Move a declared item to a position of the declarative region. 

1526 """ 

1527 from pyVHDLModel.Subprogram import Function, Procedure 

1528 

1529 if declaredItem is None: 

1530 raise ValueError("Parameter 'declaredItem' is None.") 

1531 elif not isinstance(declaredItem, ModelEntity): 

1532 ex = TypeError("Parameter 'declaredItem' is not of type 'ModelEntity'.") 

1533 ex.add_note(f"Got type '{getFullyQualifiedName(declaredItem)}'.") 

1534 raise ex 

1535 

1536 for index, item in enumerate(self._declaredItems): 

1537 if item is declaredItem: 

1538 break 

1539 else: 

1540 identifiers = "" 

1541 if isinstance(declaredItem, (NamedEntityMixin, MultipleNamedEntityMixin)): 1541 ↛ 1543line 1541 didn't jump to line 1543 because the condition on line 1541 was always true

1542 identifiers = f" '{', '.join(identifiersOf(declaredItem))}'" 

1543 raise NotAChildError( 

1544 f"{getTypeName(declaredItem)}{identifiers} is not a declared item of this {getTypeName(self)}.", 

1545 element=declaredItem, 

1546 parent=self 

1547 ) 

1548 

1549 reindex = False 

1550 if self._areDeclaredItemsIndexed: 

1551 isOverload = isinstance(declaredItem, (Function, Procedure)) 

1552 reindex = any( 

1553 not (isOverload and isinstance(item, (Function, Procedure))) 

1554 for item in self._DeclaredItemsSharingIdentifiers(declaredItem) 

1555 ) 

1556 self._UnindexDeclaredItem(declaredItem) 

1557 

1558 del self._declaredItems[index] 

1559 declaredItem._parent = None 

1560 

1561 if reindex: 

1562 self.IndexDeclaredItems() 

1563 

1564 def MoveDeclaredItem(self, declaredItem: ModelEntity, index: int) -> None: 

1565 """ 

1566 Move a declared item to a position of the declarative region. 

1567 

1568 The declared item stays a declared item of this declarative region: only the order of the declared items changes. 

1569 

1570 If the region is indexed, the lookup tables and the namespace follow the new order: a list of overloads (e.g. in 

1571 ``Functions``) follows the declaration order, and the namespace refers to the overload declared last. If 

1572 another declaration shares an identifier with the declared item (e.g. a duplicate declaration read by a parser), 

1573 the region is indexed again (:meth:`IndexDeclaredItems`) after the move. 

1574 

1575 :param declaredItem: The declared item to move. 

1576 :param index: Position of the declared item after the move, from ``0`` (first) to the number of 

1577 declared items minus one (last). A negative index counts from the end, as for a 

1578 :class:`list` index: ``-1`` moves the declared item to the end. 

1579 :raises ValueError: If parameter 'declaredItem' is ``None``. 

1580 :raises TypeError: If parameter 'declaredItem' is not of type :class:`~pyVHDLModel.Base.ModelEntity`. 

1581 :raises NotAChildError: If parameter 'declaredItem' is not a declared item of this declarative region. 

1582 :raises ValueError: If parameter 'index' is ``None``. 

1583 :raises TypeError: If parameter 'index' is not of type :class:`int`. 

1584 :raises ValueError: If parameter 'index' is out of range. 

1585 

1586 .. seealso:: 

1587 

1588 :meth:`MoveDeclaredItemBefore` 

1589 |rarr| Move a declared item before another declared item. 

1590 :meth:`MoveDeclaredItemAfter` 

1591 |rarr| Move a declared item after another declared item. 

1592 :meth:`InsertDeclaredItem` 

1593 |rarr| Insert a declared item at a position of the declarative region. 

1594 :meth:`RemoveDeclaredItem` 

1595 |rarr| Remove a declared item from the declarative region. 

1596 """ 

1597 from pyVHDLModel.Subprogram import Function, Procedure 

1598 

1599 if declaredItem is None: 

1600 raise ValueError("Parameter 'declaredItem' is None.") 

1601 elif not isinstance(declaredItem, ModelEntity): 

1602 ex = TypeError("Parameter 'declaredItem' is not of type 'ModelEntity'.") 

1603 ex.add_note(f"Got type '{getFullyQualifiedName(declaredItem)}'.") 

1604 raise ex 

1605 

1606 for currentIndex, item in enumerate(self._declaredItems): 

1607 if item is declaredItem: 

1608 break 

1609 else: 

1610 identifiers = "" 

1611 if isinstance(declaredItem, (NamedEntityMixin, MultipleNamedEntityMixin)): 1611 ↛ 1613line 1611 didn't jump to line 1613 because the condition on line 1611 was always true

1612 identifiers = f" '{', '.join(identifiersOf(declaredItem))}'" 

1613 raise NotAChildError( 

1614 f"{getTypeName(declaredItem)}{identifiers} is not a declared item of this {getTypeName(self)}.", 

1615 element=declaredItem, 

1616 parent=self 

1617 ) 

1618 

1619 if index is None: 

1620 raise ValueError("Parameter 'index' is None.") 

1621 elif not isinstance(index, int): 

1622 ex = TypeError("Parameter 'index' is not of type 'int'.") 

1623 ex.add_note(f"Got type '{getFullyQualifiedName(index)}'.") 

1624 raise ex 

1625 elif not (-len(self._declaredItems) <= index < len(self._declaredItems)): 

1626 ex = ValueError(f"Parameter 'index' is out of range {-len(self._declaredItems)}..{len(self._declaredItems) - 1}.") 

1627 ex.add_note(f"Got value '{index}'.") 

1628 raise ex 

1629 

1630 newIndex = index if index >= 0 else len(self._declaredItems) + index 

1631 if newIndex == currentIndex: 

1632 return 

1633 

1634 reindex = False 

1635 overloads = False 

1636 if self._areDeclaredItemsIndexed and len(sharingItems := self._DeclaredItemsSharingIdentifiers(declaredItem)) > 0: 

1637 isOverload = isinstance(declaredItem, (Function, Procedure)) 

1638 reindex = any(not (isOverload and isinstance(item, (Function, Procedure))) for item in sharingItems) 

1639 overloads = not reindex 

1640 

1641 if overloads: 

1642 self._RemoveOverloadFromNamespace(declaredItem) 

1643 

1644 del self._declaredItems[currentIndex] 

1645 self._declaredItems.insert(newIndex, declaredItem) 

1646 

1647 if reindex: 

1648 self.IndexDeclaredItems() 

1649 elif overloads: 

1650 self._IndexOverload(declaredItem) 

1651 

1652 def MoveDeclaredItemBefore(self, declaredItem: ModelEntity, before: ModelEntity) -> None: 

1653 """ 

1654 Move a declared item before another declared item. 

1655 

1656 The declared item stays a declared item of this declarative region: only the order of the declared items changes, 

1657 as for :meth:`MoveDeclaredItem`. Moving a declared item before itself keeps it in place. 

1658 

1659 :param declaredItem: The declared item to move. 

1660 :param before: The declared item of this declarative region to move before. 

1661 :raises ValueError: If parameter 'before' is ``None``. 

1662 :raises TypeError: If parameter 'before' is not of type :class:`~pyVHDLModel.Base.ModelEntity`. 

1663 :raises NotAChildError: If parameter 'before' is not a declared item of this declarative region. 

1664 :raises ValueError: If parameter 'declaredItem' is ``None``. 

1665 :raises TypeError: If parameter 'declaredItem' is not of type :class:`~pyVHDLModel.Base.ModelEntity`. 

1666 :raises NotAChildError: If parameter 'declaredItem' is not a declared item of this declarative region. 

1667 

1668 .. seealso:: 

1669 

1670 :meth:`MoveDeclaredItem` 

1671 |rarr| Move a declared item to a position of the declarative region. 

1672 :meth:`MoveDeclaredItemAfter` 

1673 |rarr| Move a declared item after another declared item. 

1674 """ 

1675 if before is None: 

1676 raise ValueError("Parameter 'before' is None.") 

1677 elif not isinstance(before, ModelEntity): 

1678 ex = TypeError("Parameter 'before' is not of type 'ModelEntity'.") 

1679 ex.add_note(f"Got type '{getFullyQualifiedName(before)}'.") 

1680 raise ex 

1681 

1682 for index, item in enumerate(self._declaredItems): 

1683 if item is before: 

1684 break 

1685 else: 

1686 identifiers = "" 

1687 if isinstance(before, (NamedEntityMixin, MultipleNamedEntityMixin)): 1687 ↛ 1689line 1687 didn't jump to line 1689 because the condition on line 1687 was always true

1688 identifiers = f" '{', '.join(identifiersOf(before))}'" 

1689 raise NotAChildError( 

1690 f"{getTypeName(before)}{identifiers} is not a declared item of this {getTypeName(self)}.", 

1691 element=before, 

1692 parent=self 

1693 ) 

1694 

1695 precedes = any(item is declaredItem for item in self._declaredItems[:index]) 

1696 self.MoveDeclaredItem(declaredItem, index - 1 if precedes else index) 

1697 

1698 def MoveDeclaredItemAfter(self, declaredItem: ModelEntity, after: ModelEntity) -> None: 

1699 """ 

1700 Move a declared item after another declared item. 

1701 

1702 The declared item stays a declared item of this declarative region: only the order of the declared items changes, 

1703 as for :meth:`MoveDeclaredItem`. Moving a declared item after itself keeps it in place. 

1704 

1705 :param declaredItem: The declared item to move. 

1706 :param after: The declared item of this declarative region to move after. 

1707 :raises ValueError: If parameter 'after' is ``None``. 

1708 :raises TypeError: If parameter 'after' is not of type :class:`~pyVHDLModel.Base.ModelEntity`. 

1709 :raises NotAChildError: If parameter 'after' is not a declared item of this declarative region. 

1710 :raises ValueError: If parameter 'declaredItem' is ``None``. 

1711 :raises TypeError: If parameter 'declaredItem' is not of type :class:`~pyVHDLModel.Base.ModelEntity`. 

1712 :raises NotAChildError: If parameter 'declaredItem' is not a declared item of this declarative region. 

1713 

1714 .. seealso:: 

1715 

1716 :meth:`MoveDeclaredItem` 

1717 |rarr| Move a declared item to a position of the declarative region. 

1718 :meth:`MoveDeclaredItemBefore` 

1719 |rarr| Move a declared item before another declared item. 

1720 """ 

1721 if after is None: 

1722 raise ValueError("Parameter 'after' is None.") 

1723 elif not isinstance(after, ModelEntity): 1723 ↛ 1724line 1723 didn't jump to line 1724 because the condition on line 1723 was never true

1724 ex = TypeError("Parameter 'after' is not of type 'ModelEntity'.") 

1725 ex.add_note(f"Got type '{getFullyQualifiedName(after)}'.") 

1726 raise ex 

1727 

1728 for index, item in enumerate(self._declaredItems): 

1729 if item is after: 

1730 break 

1731 else: 

1732 identifiers = "" 

1733 if isinstance(after, (NamedEntityMixin, MultipleNamedEntityMixin)): 1733 ↛ 1735line 1733 didn't jump to line 1735 because the condition on line 1733 was always true

1734 identifiers = f" '{', '.join(identifiersOf(after))}'" 

1735 raise NotAChildError( 

1736 f"{getTypeName(after)}{identifiers} is not a declared item of this {getTypeName(self)}.", 

1737 element=after, 

1738 parent=self 

1739 ) 

1740 

1741 precedes = any(item is declaredItem for item in self._declaredItems[:index + 1]) 

1742 self.MoveDeclaredItem(declaredItem, index if precedes else index + 1) 

1743 

1744 

1745@export 

1746class WithoutStatementsMixin(metaclass=ExtendedType, mixin=True): 

1747 """ 

1748 A mixin-class for language constructs with a declarative region, but without statements. 

1749 

1750 It provides the labeled statements ``_Labels()`` the declaration region expects from its host class: none. 

1751 

1752 .. seealso:: 

1753 

1754 * :class:`Package <pyVHDLModel.DesignUnit.Package>` 

1755 * :class:`Package body <pyVHDLModel.DesignUnit.PackageBody>` 

1756 * :class:`Protected type <pyVHDLModel.Type.ProtectedType>` 

1757 * :class:`Protected type body <pyVHDLModel.Type.ProtectedTypeBody>` 

1758 """ 

1759 

1760 def _Labels(self) -> tuple[ModelEntity, ...]: 

1761 """ 

1762 Return the labeled statements, whose labels are declared in this declarative region. 

1763 

1764 :returns: An empty tuple. 

1765 """ 

1766 return () 

1767 

1768 

1769@export 

1770class ConcurrentDeclarationRegionMixin(DeclarationRegionMixin, mixin=True): 

1771 # FIXME: define list prefix type e.g. via Union 

1772 """ 

1773 A mixin-class for concurrent declaration regions. 

1774 

1775 Entities, architectures, packages, blocks and generate bodies declare items concurrently. Beside 

1776 the namespace, the region keeps a lookup table per kind of declared item (:data:`Types`, 

1777 :data:`Signals`, :data:`Constants`, ...). 

1778 

1779 .. seealso:: 

1780 

1781 * :class:`Concurrent block statement <pyVHDLModel.Concurrent.ConcurrentBlockStatement>` 

1782 * :class:`Generate branch <pyVHDLModel.Concurrent.GenerateBranch>` 

1783 * :class:`Concurrent case <pyVHDLModel.Concurrent.ConcurrentCase>` 

1784 * :class:`For generate statement <pyVHDLModel.Concurrent.ForGenerateStatement>` 

1785 * :class:`Package <pyVHDLModel.DesignUnit.Package>` 

1786 * :class:`Package body <pyVHDLModel.DesignUnit.PackageBody>` 

1787 * :class:`Entity <pyVHDLModel.DesignUnit.Entity>` 

1788 * :class:`Architecture <pyVHDLModel.DesignUnit.Architecture>` 

1789 * :class:`Sequential declaration region <pyVHDLModel.Regions.SequentialDeclarationRegionMixin>` 

1790 * :class:`Namespace <pyVHDLModel.Namespace.Namespace>` 

1791 """ 

1792 _declaredItems: list #: List of all declared items in this concurrent declaration region. 

1793 _namespace: Namespace #: The namespace of this concurrent declaration region. 

1794 

1795 # _attributes: dict[str, Attribute] 

1796 # _aliases: dict[str, Alias] 

1797 _types: dict[str, FullType] #: Dictionary of all types declared in this concurrent declaration region. 

1798 _subtypes: dict[str, Subtype] #: Dictionary of all subtypes declared in this concurrent declaration region. 

1799 # _objects: dict[str, Union[Constant, Variable, Signal]] 

1800 _constants: dict[str, Constant] #: Dictionary of all constants declared in this concurrent declaration region. 

1801 _signals: dict[str, Signal] #: Dictionary of all signals declared in this concurrent declaration region. 

1802 _sharedVariables: dict[str, SharedVariable] #: Dictionary of all shared variables declared in this concurrent declaration region. 

1803 _files: dict[str, File] #: Dictionary of all files declared in this concurrent declaration region. 

1804 # _subprograms: dict[str, list[Subprogram]] #: Dictionary of all subprograms declared in this concurrent declaration region. 

1805 # FIXME: overloads are only collected into a list, not matched/resolved by signature. 

1806 _functions: dict[str, list[Function]] #: Dictionary of all functions declared in this concurrent declaration region, indexed by name; each entry is a list of overloads. 

1807 _procedures: dict[str, list[Procedure]] #: Dictionary of all procedures declared in this concurrent declaration region, indexed by name; each entry is a list of overloads. 

1808 _components: dict[str, Any] #: Dictionary of all components declared in this concurrent declaration region. 

1809 

1810 def __init__( 

1811 self, 

1812 namespaceName: Nullable[str] = None, 

1813 declaredItems: Nullable[Iterable] = None, 

1814 sharesRegionWithParent: bool = False 

1815 ) -> None: 

1816 # TODO: extract to mixin 

1817 """ 

1818 Initializes a concurrent declaration region. 

1819 

1820 :param namespaceName: Name of this region's namespace, usually the host's label or identifier. 

1821 :param declaredItems: List of all declared items in this concurrent declaration region. 

1822 :param sharesRegionWithParent: Optional, ``True`` if this region continues its parent's declarative region (an 

1823 architecture its entity's, a package body its package's). Default: ``False``. 

1824 """ 

1825 DeclarationRegionMixin.__init__(self) 

1826 

1827 self._namespace = Namespace(namespaceName, sharesRegionWithParent=sharesRegionWithParent) 

1828 

1829 self._declaredItems = [] # TODO: convert to dict 

1830 if declaredItems is not None: 

1831 for item in declaredItems: 

1832 self._declaredItems.append(item) 

1833 item.Parent = self 

1834 

1835 self._types = {} 

1836 self._subtypes = {} 

1837 # self._objects = {} 

1838 self._constants = {} 

1839 self._signals = {} 

1840 self._sharedVariables = {} 

1841 self._files = {} 

1842 # self._subprograms = {} 

1843 self._functions = {} 

1844 self._procedures = {} 

1845 self._components = {} 

1846 

1847 @readonly 

1848 def DeclaredItems(self) -> list: 

1849 """ 

1850 Read-only property to access the declared items (:attr:`_declaredItems`). 

1851 

1852 :returns: List of declared items. 

1853 """ 

1854 return self._declaredItems 

1855 

1856 @readonly 

1857 def Types(self) -> dict[str, FullType]: 

1858 """ 

1859 Read-only property to access the types (:attr:`_types`). 

1860 

1861 :returns: Dictionary of types, indexed by normalized identifier. 

1862 """ 

1863 return self._types 

1864 

1865 @readonly 

1866 def Subtypes(self) -> dict[str, Subtype]: 

1867 """ 

1868 Read-only property to access the subtypes (:attr:`_subtypes`). 

1869 

1870 :returns: Dictionary of subtypes, indexed by normalized identifier. 

1871 """ 

1872 return self._subtypes 

1873 

1874 # @readonly 

1875 # def Objects(self) -> dict[str, Union[Constant, SharedVariable, Signal, File]]: 

1876 # return self._objects 

1877 

1878 @readonly 

1879 def Constants(self) -> dict[str, Constant]: 

1880 """ 

1881 Read-only property to access the constants (:attr:`_constants`). 

1882 

1883 :returns: Dictionary of constants, indexed by normalized identifier. 

1884 """ 

1885 return self._constants 

1886 

1887 @readonly 

1888 def Signals(self) -> dict[str, Signal]: 

1889 """ 

1890 Read-only property to access the signals (:attr:`_signals`). 

1891 

1892 :returns: Dictionary of signals, indexed by normalized identifier. 

1893 """ 

1894 return self._signals 

1895 

1896 @readonly 

1897 def SharedVariables(self) -> dict[str, SharedVariable]: 

1898 """ 

1899 Read-only property to access the shared variables (:attr:`_sharedVariables`). 

1900 

1901 :returns: Dictionary of shared variables, indexed by normalized identifier. 

1902 """ 

1903 return self._sharedVariables 

1904 

1905 @readonly 

1906 def Files(self) -> dict[str, File]: 

1907 """ 

1908 Read-only property to access the files (:attr:`_files`). 

1909 

1910 :returns: Dictionary of files, indexed by normalized identifier. 

1911 """ 

1912 return self._files 

1913 

1914 # @readonly 

1915 # def Subprograms(self) -> dict[str, Subprogram]: 

1916 # return self._subprograms 

1917 

1918 @readonly 

1919 def Functions(self) -> dict[str, list[Function]]: 

1920 """ 

1921 Read-only property to access the functions (:attr:`_functions`). 

1922 

1923 :returns: Dictionary of functions, indexed by normalized identifier; each entry is a list of overloads. 

1924 """ 

1925 return self._functions 

1926 

1927 @readonly 

1928 def Procedures(self) -> dict[str, list[Procedure]]: 

1929 """ 

1930 Read-only property to access the procedures (:attr:`_procedures`). 

1931 

1932 :returns: Dictionary of procedures, indexed by normalized identifier; each entry is a list of overloads. 

1933 """ 

1934 return self._procedures 

1935 

1936 @readonly 

1937 def Components(self) -> dict[str, Any]: 

1938 """ 

1939 Read-only property to access the components (:attr:`_components`). 

1940 

1941 :returns: Dictionary of components, indexed by normalized identifier. 

1942 """ 

1943 return self._components 

1944 

1945 def _Declarations(self) -> tuple[ModelEntity, ...]: 

1946 """ 

1947 Return all declarations of this declarative region. 

1948 

1949 A host class with interface items extends the list by its interface items, listed first. 

1950 

1951 :returns: Tuple of declarations: the declared items, in declaration order. 

1952 """ 

1953 return tuple(self._declaredItems) 

1954 

1955 def IndexDeclaredItems(self) -> None: 

1956 """ 

1957 Index declared items listed in the concurrent declaration region. 

1958 

1959 .. rubric:: Algorithm 

1960 

1961 1. Clear all lookup tables and remove all declared items from :attr:`_namespace`. Interface items and imported 

1962 items stay in the namespace. 

1963 2. Iterate all declared items: 

1964 

1965 * Every declared item is added to :attr:`_namespace`, except a protected type body. 

1966 * If the declared item is a :class:`~pyVHDLModel.Type.ProtectedTypeBody`, skip it: it completes its protected 

1967 type. 

1968 * If the declared item is a :class:`~pyVHDLModel.Type.FullType`, then add an entry to :attr:`_types`. 

1969 * If the declared item is a :class:`~pyVHDLModel.Type.Subtype`, then add an entry to :attr:`_subtypes`. 

1970 * If the declared item is a :class:`~pyVHDLModel.Subprogram.Function`, then add an entry to :attr:`_functions`. 

1971 * If the declared item is a :class:`~pyVHDLModel.Subprogram.Procedure`, then add an entry to :attr:`_procedures`. 

1972 * If the declared item is a :class:`~pyVHDLModel.Object.Constant`, then add an entry to :attr:`_constants`. 

1973 * If the declared item is a :class:`~pyVHDLModel.Object.Signal`, then add an entry to :attr:`_signals`. 

1974 * If the declared item is a :class:`~pyVHDLModel.Object.Variable`, TODO. 

1975 * If the declared item is a :class:`~pyVHDLModel.Object.SharedVariable`, then add an entry to :attr:`_sharedVariables`. 

1976 * If the declared item is a :class:`~pyVHDLModel.Object.File`, then add an entry to :attr:`_files`. 

1977 * If the declared item is neither of these types, call :meth:`_IndexOtherDeclaredItem`. |br| 

1978 Derived classes may override this virtual function. 

1979 

1980 Indexing again yields the same lookup tables and namespace. 

1981 

1982 .. seealso:: 

1983 

1984 :meth:`pyVHDLModel.Design.IndexPackages` 

1985 Iterate all packages in the design and index declared items. 

1986 :meth:`pyVHDLModel.Library.IndexPackages` 

1987 Iterate all packages in the library and index declared items. 

1988 :meth:`pyVHDLModel.Library._IndexOtherDeclaredItem` 

1989 Iterate all packages in the library and index declared items. 

1990 

1991 :raises UnhandledCriticalWarningError: If an identifier is declared twice in a declarative region and no 

1992 :class:`~pyTooling.Warning.WarningCollector` collects the 

1993 :exc:`~pyVHDLModel.Exception.DuplicateDeclarationWarning`. 

1994 """ 

1995 self._types.clear() 

1996 self._subtypes.clear() 

1997 self._constants.clear() 

1998 self._signals.clear() 

1999 self._sharedVariables.clear() 

2000 self._files.clear() 

2001 self._functions.clear() 

2002 self._procedures.clear() 

2003 self._components.clear() 

2004 self._RemoveDeclaredItemsFromNamespace() 

2005 

2006 for item in self._declaredItems: 

2007 self._IndexDeclaredItem(item) 

2008 

2009 self._areDeclaredItemsIndexed = True 

2010 

2011 def _IndexDeclaredItem(self, item: ModelEntity) -> None: 

2012 """ 

2013 Add a declared item to the namespace and to the lookup table matching its kind. 

2014 

2015 :param item: The declared item. 

2016 """ 

2017 from pyVHDLModel.DesignUnit import Component 

2018 from pyVHDLModel.Subprogram import Function, Procedure 

2019 from pyVHDLModel.Type import Subtype, FullType, ProtectedTypeBody 

2020 

2021 if isinstance(item, ProtectedTypeBody): 

2022 # A protected type body completes its protected type. 

2023 pass 

2024 elif isinstance(item, FullType): 

2025 self._types[item._normalizedIdentifier] = item 

2026 self._namespace.AddElement(item._normalizedIdentifier, item) 

2027 elif isinstance(item, Subtype): 

2028 self._subtypes[item._normalizedIdentifier] = item 

2029 self._namespace.AddElement(item._normalizedIdentifier, item) 

2030 elif isinstance(item, Function): 

2031 # FIXME: overloads are only appended to a list, not matched/resolved by signature (no 

2032 # real overload resolution yet). 

2033 self._functions.setdefault(item._normalizedIdentifier, []).append(item) 

2034 self._namespace.AddElement(item._normalizedIdentifier, item, overloadable=True) 

2035 elif isinstance(item, Procedure): 

2036 # FIXME: overloads are only appended to a list, not matched/resolved by signature (no 

2037 # real overload resolution yet). 

2038 self._procedures.setdefault(item._normalizedIdentifier, []).append(item) 

2039 self._namespace.AddElement(item._normalizedIdentifier, item, overloadable=True) 

2040 elif isinstance(item, Constant): 

2041 for normalizedIdentifier in item._normalizedIdentifiers: 

2042 self._constants[normalizedIdentifier] = item 

2043 self._namespace.AddElement(normalizedIdentifier, item) 

2044 # self._objects[normalizedIdentifier] = item 

2045 elif isinstance(item, Signal): 

2046 for normalizedIdentifier in item._normalizedIdentifiers: 

2047 self._signals[normalizedIdentifier] = item 

2048 self._namespace.AddElement(normalizedIdentifier, item) 

2049 elif isinstance(item, Variable): 

2050 # TODO: variables declared in a concurrent declaration region (e.g. shared variables outside a 

2051 # protected type) are not yet indexed into a dedicated namespace/lookup table. 

2052 identifiers = ", ".join(f"'{i}'" for i in item._identifiers) 

2053 WarningCollector.Raise( 

2054 NotImplementedWarning(f"IndexDeclaredItems: variable(s) {identifiers} are not yet indexed.", item=item) 

2055 ) 

2056 elif isinstance(item, SharedVariable): 

2057 for normalizedIdentifier in item._normalizedIdentifiers: 

2058 self._sharedVariables[normalizedIdentifier] = item 

2059 self._namespace.AddElement(normalizedIdentifier, item) 

2060 elif isinstance(item, File): 

2061 for normalizedIdentifier in item._normalizedIdentifiers: 

2062 self._files[normalizedIdentifier] = item 

2063 self._namespace.AddElement(normalizedIdentifier, item) 

2064 elif isinstance(item, Component): 

2065 self._components[item._normalizedIdentifier] = item 

2066 self._namespace.AddElement(item._normalizedIdentifier, item) 

2067 else: 

2068 self._IndexOtherDeclaredItem(item) 

2069 

2070 def _UnindexDeclaredItem(self, item: ModelEntity) -> None: 

2071 """ 

2072 Remove a declared item from the namespace and from the lookup table matching its kind. 

2073 

2074 :param item: The declared item. 

2075 """ 

2076 from pyVHDLModel.DesignUnit import Component 

2077 from pyVHDLModel.Subprogram import Function, Procedure 

2078 from pyVHDLModel.Type import Subtype, FullType, ProtectedTypeBody 

2079 

2080 if isinstance(item, ProtectedTypeBody): 2080 ↛ 2081line 2080 didn't jump to line 2081 because the condition on line 2080 was never true

2081 pass 

2082 elif isinstance(item, FullType): 

2083 self._RemoveDeclaredItemEntries(self._types, item) 

2084 elif isinstance(item, Subtype): 2084 ↛ 2085line 2084 didn't jump to line 2085 because the condition on line 2084 was never true

2085 self._RemoveDeclaredItemEntries(self._subtypes, item) 

2086 elif isinstance(item, (Function, Procedure)): 

2087 self._UnindexOverload(item) 

2088 elif isinstance(item, Constant): 2088 ↛ 2089line 2088 didn't jump to line 2089 because the condition on line 2088 was never true

2089 self._RemoveDeclaredItemEntries(self._constants, item) 

2090 elif isinstance(item, Signal): 

2091 self._RemoveDeclaredItemEntries(self._signals, item) 

2092 elif isinstance(item, Variable): 2092 ↛ 2093line 2092 didn't jump to line 2093 because the condition on line 2092 was never true

2093 pass 

2094 elif isinstance(item, SharedVariable): 2094 ↛ 2095line 2094 didn't jump to line 2095 because the condition on line 2094 was never true

2095 self._RemoveDeclaredItemEntries(self._sharedVariables, item) 

2096 elif isinstance(item, File): 2096 ↛ 2097line 2096 didn't jump to line 2097 because the condition on line 2096 was never true

2097 self._RemoveDeclaredItemEntries(self._files, item) 

2098 elif isinstance(item, Component): 

2099 self._RemoveDeclaredItemEntries(self._components, item) 

2100 else: 

2101 self._UnindexOtherDeclaredItem(item) 

2102 

2103 

2104@export 

2105class SequentialDeclarationRegionMixin(DeclarationRegionMixin, mixin=True): 

2106 """ 

2107 A mixin-class for sequential declaration regions: process statements and subprogram bodies. 

2108 

2109 .. note:: 

2110 

2111 VHDL's ``process_declarative_item`` and ``subprogram_declarative_item`` rules are identical, so both 

2112 regions share this implementation. Compared to a concurrent region 

2113 (:class:`ConcurrentDeclarationRegionMixin`, ``block_declarative_item``), a sequential region can 

2114 declare a **variable**, but no signal, shared variable, component or mode view, and none of the 

2115 specifications. 

2116 

2117 .. seealso:: 

2118 

2119 * :class:`Process statement <pyVHDLModel.Concurrent.ProcessStatement>` 

2120 * :class:`Subprogram <pyVHDLModel.Subprogram.Subprogram>` 

2121 * :class:`Concurrent declaration region <pyVHDLModel.Regions.ConcurrentDeclarationRegionMixin>` 

2122 * :class:`Namespace <pyVHDLModel.Namespace.Namespace>` 

2123 """ 

2124 

2125 _declaredItems: list #: List of all declared items in this sequential declaration region. 

2126 _namespace: Namespace #: The namespace of this sequential declaration region. 

2127 

2128 _types: dict[str, FullType] #: Dictionary of all types declared in this sequential declaration region. 

2129 _subtypes: dict[str, Subtype] #: Dictionary of all subtypes declared in this sequential declaration region. 

2130 _constants: dict[str, Constant] #: Dictionary of all constants declared in this sequential declaration region. 

2131 _variables: dict[str, Variable] #: Dictionary of all variables declared in this sequential declaration region. 

2132 _files: dict[str, File] #: Dictionary of all files declared in this sequential declaration region. 

2133 # FIXME: overloads are only collected into a list, not matched/resolved by signature. 

2134 _functions: dict[str, list[Function]] #: Dictionary of all functions declared in this sequential declaration region, indexed by name; each entry is a list of overloads. 

2135 _procedures: dict[str, list[Procedure]] #: Dictionary of all procedures declared in this sequential declaration region, indexed by name; each entry is a list of overloads. 

2136 

2137 def __init__(self, namespaceName: Nullable[str] = None, declaredItems: Nullable[Iterable] = None) -> None: 

2138 """ 

2139 Initialize a sequential declaration region. 

2140 

2141 :param namespaceName: Name of this region's namespace, usually the host's label or identifier. 

2142 :param declaredItems: The items declared in this region. 

2143 """ 

2144 DeclarationRegionMixin.__init__(self) 

2145 

2146 self._namespace = Namespace(namespaceName) 

2147 

2148 self._declaredItems = [] # TODO: convert to dict 

2149 if declaredItems is not None: 

2150 for item in declaredItems: 

2151 self._declaredItems.append(item) 

2152 item.Parent = self 

2153 

2154 self._types = {} 

2155 self._subtypes = {} 

2156 self._constants = {} 

2157 self._variables = {} 

2158 self._files = {} 

2159 self._functions = {} 

2160 self._procedures = {} 

2161 

2162 @readonly 

2163 def DeclaredItems(self) -> list: 

2164 """ 

2165 Read-only property to access the declared items (:attr:`_declaredItems`). 

2166 

2167 :returns: List of declared items. 

2168 """ 

2169 return self._declaredItems 

2170 

2171 @readonly 

2172 def Namespace(self) -> Namespace: 

2173 """ 

2174 Read-only property to access this region's namespace (:attr:`_namespace`). 

2175 

2176 :returns: The namespace. 

2177 """ 

2178 return self._namespace 

2179 

2180 @readonly 

2181 def Types(self) -> dict[str, FullType]: 

2182 """ 

2183 Read-only property to access the declared types (:attr:`_types`). 

2184 

2185 :returns: Dictionary of types, indexed by normalized identifier. 

2186 """ 

2187 return self._types 

2188 

2189 @readonly 

2190 def Subtypes(self) -> dict[str, Subtype]: 

2191 """ 

2192 Read-only property to access the declared subtypes (:attr:`_subtypes`). 

2193 

2194 :returns: Dictionary of subtypes, indexed by normalized identifier. 

2195 """ 

2196 return self._subtypes 

2197 

2198 @readonly 

2199 def Constants(self) -> dict[str, Constant]: 

2200 """ 

2201 Read-only property to access the declared constants (:attr:`_constants`). 

2202 

2203 :returns: Dictionary of constants, indexed by normalized identifier. 

2204 """ 

2205 return self._constants 

2206 

2207 @readonly 

2208 def Variables(self) -> dict[str, Variable]: 

2209 """ 

2210 Read-only property to access the declared variables (:attr:`_variables`). 

2211 

2212 :returns: Dictionary of variables, indexed by normalized identifier. 

2213 """ 

2214 return self._variables 

2215 

2216 @readonly 

2217 def Files(self) -> dict[str, File]: 

2218 """ 

2219 Read-only property to access the declared files (:attr:`_files`). 

2220 

2221 :returns: Dictionary of files, indexed by normalized identifier. 

2222 """ 

2223 return self._files 

2224 

2225 @readonly 

2226 def Functions(self) -> dict[str, list[Function]]: 

2227 """ 

2228 Read-only property to access the declared functions (:attr:`_functions`). 

2229 

2230 :returns: Dictionary of functions, indexed by normalized identifier; each entry is a list of overloads. 

2231 """ 

2232 return self._functions 

2233 

2234 @readonly 

2235 def Procedures(self) -> dict[str, list[Procedure]]: 

2236 """ 

2237 Read-only property to access the declared procedures (:attr:`_procedures`). 

2238 

2239 :returns: Dictionary of procedures, indexed by normalized identifier; each entry is a list of overloads. 

2240 """ 

2241 return self._procedures 

2242 

2243 def _Declarations(self) -> tuple[ModelEntity, ...]: 

2244 """ 

2245 Return all declarations of this declarative region. 

2246 

2247 A host class with interface items extends the list by its interface items, listed first. 

2248 

2249 :returns: Tuple of declarations: the declared items, in declaration order. 

2250 """ 

2251 return tuple(self._declaredItems) 

2252 

2253 def IndexDeclaredItems(self) -> None: 

2254 """ 

2255 Index declared items listed in the sequential declaration region. 

2256 

2257 All lookup tables are cleared and all declared items are removed from :attr:`_namespace` first. Then every declared 

2258 item is added to :attr:`_namespace`, and additionally to the lookup table matching its kind. Items of an unhandled 

2259 kind are passed to :meth:`_IndexOtherDeclaredItem`. A protected type body completes its protected type, so it's 

2260 skipped. Indexing again yields the same lookup tables and namespace. 

2261 

2262 .. seealso:: 

2263 

2264 :meth:`ConcurrentDeclarationRegionMixin.IndexDeclaredItems` 

2265 The same algorithm for a concurrent declaration region. 

2266 

2267 :raises UnhandledCriticalWarningError: If an identifier is declared twice in a declarative region and no 

2268 :class:`~pyTooling.Warning.WarningCollector` collects the 

2269 :exc:`~pyVHDLModel.Exception.DuplicateDeclarationWarning`. 

2270 """ 

2271 self._types.clear() 

2272 self._subtypes.clear() 

2273 self._constants.clear() 

2274 self._variables.clear() 

2275 self._files.clear() 

2276 self._functions.clear() 

2277 self._procedures.clear() 

2278 self._RemoveDeclaredItemsFromNamespace() 

2279 

2280 for item in self._declaredItems: 

2281 self._IndexDeclaredItem(item) 

2282 

2283 self._areDeclaredItemsIndexed = True 

2284 

2285 def _IndexDeclaredItem(self, item: ModelEntity) -> None: 

2286 """ 

2287 Add a declared item to the namespace and to the lookup table matching its kind. 

2288 

2289 :param item: The declared item. 

2290 """ 

2291 from pyVHDLModel.Subprogram import Function, Procedure 

2292 from pyVHDLModel.Type import Subtype, FullType, ProtectedTypeBody 

2293 

2294 if isinstance(item, ProtectedTypeBody): 

2295 # A protected type body completes its protected type. 

2296 pass 

2297 elif isinstance(item, FullType): 

2298 self._types[item._normalizedIdentifier] = item 

2299 self._namespace.AddElement(item._normalizedIdentifier, item) 

2300 elif isinstance(item, Subtype): 

2301 self._subtypes[item._normalizedIdentifier] = item 

2302 self._namespace.AddElement(item._normalizedIdentifier, item) 

2303 elif isinstance(item, Function): 2303 ↛ 2306line 2303 didn't jump to line 2306 because the condition on line 2303 was never true

2304 # FIXME: overloads are only appended to a list, not matched/resolved by signature (no 

2305 # real overload resolution yet). 

2306 self._functions.setdefault(item._normalizedIdentifier, []).append(item) 

2307 self._namespace.AddElement(item._normalizedIdentifier, item, overloadable=True) 

2308 elif isinstance(item, Procedure): 

2309 # FIXME: overloads are only appended to a list, not matched/resolved by signature (no 

2310 # real overload resolution yet). 

2311 self._procedures.setdefault(item._normalizedIdentifier, []).append(item) 

2312 self._namespace.AddElement(item._normalizedIdentifier, item, overloadable=True) 

2313 elif isinstance(item, Constant): 

2314 for normalizedIdentifier in item._normalizedIdentifiers: 

2315 self._constants[normalizedIdentifier] = item 

2316 self._namespace.AddElement(normalizedIdentifier, item) 

2317 elif isinstance(item, Variable): 

2318 for normalizedIdentifier in item._normalizedIdentifiers: 

2319 self._variables[normalizedIdentifier] = item 

2320 self._namespace.AddElement(normalizedIdentifier, item) 

2321 elif isinstance(item, File): 2321 ↛ 2326line 2321 didn't jump to line 2326 because the condition on line 2321 was always true

2322 for normalizedIdentifier in item._normalizedIdentifiers: 

2323 self._files[normalizedIdentifier] = item 

2324 self._namespace.AddElement(normalizedIdentifier, item) 

2325 else: 

2326 self._IndexOtherDeclaredItem(item) 

2327 

2328 def _UnindexDeclaredItem(self, item: ModelEntity) -> None: 

2329 """ 

2330 Remove a declared item from the namespace and from the lookup table matching its kind. 

2331 

2332 :param item: The declared item. 

2333 """ 

2334 from pyVHDLModel.Subprogram import Function, Procedure 

2335 from pyVHDLModel.Type import Subtype, FullType, ProtectedTypeBody 

2336 

2337 if isinstance(item, ProtectedTypeBody): 2337 ↛ 2338line 2337 didn't jump to line 2338 because the condition on line 2337 was never true

2338 pass 

2339 elif isinstance(item, FullType): 2339 ↛ 2340line 2339 didn't jump to line 2340 because the condition on line 2339 was never true

2340 self._RemoveDeclaredItemEntries(self._types, item) 

2341 elif isinstance(item, Subtype): 2341 ↛ 2342line 2341 didn't jump to line 2342 because the condition on line 2341 was never true

2342 self._RemoveDeclaredItemEntries(self._subtypes, item) 

2343 elif isinstance(item, (Function, Procedure)): 

2344 self._UnindexOverload(item) 

2345 elif isinstance(item, Constant): 2345 ↛ 2346line 2345 didn't jump to line 2346 because the condition on line 2345 was never true

2346 self._RemoveDeclaredItemEntries(self._constants, item) 

2347 elif isinstance(item, Variable): 2347 ↛ 2349line 2347 didn't jump to line 2349 because the condition on line 2347 was always true

2348 self._RemoveDeclaredItemEntries(self._variables, item) 

2349 elif isinstance(item, File): 

2350 self._RemoveDeclaredItemEntries(self._files, item) 

2351 else: 

2352 self._UnindexOtherDeclaredItem(item) 

2353 

2354 

2355@export 

2356class ProtectedTypeDeclarationRegionMixin(DeclarationRegionMixin, mixin=True): 

2357 """ 

2358 A mixin-class for the declarative region of a protected type declaration. 

2359 

2360 VHDL's ``protected_type_declarative_item`` rule admits subprogram declarations only, making this the 

2361 narrowest declarative region in the language. A protected type *body* differs: its declarative part 

2362 matches a subprogram's, so it uses :class:`SequentialDeclarationRegionMixin`. 

2363 

2364 .. seealso:: 

2365 

2366 * :class:`Protected type <pyVHDLModel.Type.ProtectedType>` 

2367 * :class:`Protected type body <pyVHDLModel.Type.ProtectedTypeBody>` 

2368 * :class:`Sequential declaration region <pyVHDLModel.Regions.SequentialDeclarationRegionMixin>` 

2369 * :class:`Namespace <pyVHDLModel.Namespace.Namespace>` 

2370 """ 

2371 

2372 _declaredItems: list #: List of all declared items in this protected type declaration. 

2373 _namespace: Namespace #: The namespace of this protected type declaration. 

2374 

2375 # FIXME: overloads are only collected into a list, not matched/resolved by signature. 

2376 _functions: dict[str, list[Function]] #: All declared functions, indexed by name; each is a list of overloads. 

2377 _procedures: dict[str, list[Procedure]] #: All declared procedures by name; each is a list of overloads. 

2378 

2379 def __init__(self, namespaceName: Nullable[str] = None, declaredItems: Nullable[Iterable] = None) -> None: 

2380 """ 

2381 Initialize a protected type declaration region. 

2382 

2383 :param namespaceName: Name of this region's namespace, usually the protected type's identifier. 

2384 :param declaredItems: The items declared in this region. 

2385 """ 

2386 DeclarationRegionMixin.__init__(self) 

2387 

2388 self._namespace = Namespace(namespaceName) 

2389 

2390 self._declaredItems = [] # TODO: convert to dict 

2391 if declaredItems is not None: 

2392 for item in declaredItems: 

2393 self._declaredItems.append(item) 

2394 item.Parent = self 

2395 

2396 self._functions = {} 

2397 self._procedures = {} 

2398 

2399 @readonly 

2400 def DeclaredItems(self) -> list: 

2401 """ 

2402 Read-only property to access the declared items (:attr:`_declaredItems`). 

2403 

2404 :returns: List of all declared items. 

2405 """ 

2406 return self._declaredItems 

2407 

2408 @readonly 

2409 def Functions(self) -> dict[str, list[Function]]: 

2410 """ 

2411 Read-only property to access the declared functions (:attr:`_functions`). 

2412 

2413 :returns: Dictionary of all functions, indexed by name; each entry is a list of overloads. 

2414 """ 

2415 return self._functions 

2416 

2417 @readonly 

2418 def Procedures(self) -> dict[str, list[Procedure]]: 

2419 """ 

2420 Read-only property to access the declared procedures (:attr:`_procedures`). 

2421 

2422 :returns: Dictionary of all procedures, indexed by name; each entry is a list of overloads. 

2423 """ 

2424 return self._procedures 

2425 

2426 def _Declarations(self) -> tuple[ModelEntity, ...]: 

2427 """ 

2428 Return all declarations of this declarative region. 

2429 

2430 A host class with interface items extends the list by its interface items, listed first. 

2431 

2432 :returns: Tuple of declarations: the declared items, in declaration order. 

2433 """ 

2434 return tuple(self._declaredItems) 

2435 

2436 def IndexDeclaredItems(self) -> None: 

2437 """ 

2438 Index declared items listed in the protected type declaration. 

2439 

2440 Both lookup tables are cleared and all declared items are removed from :attr:`_namespace` first. Only subprogram 

2441 declarations are legal here, so anything else is passed to :meth:`_IndexOtherDeclaredItem`. Indexing again yields 

2442 the same lookup tables and namespace. 

2443 

2444 .. seealso:: 

2445 

2446 :meth:`SequentialDeclarationRegionMixin.IndexDeclaredItems` 

2447 The same algorithm for the protected type's body. 

2448 

2449 :raises UnhandledCriticalWarningError: If an identifier is declared twice in a declarative region and no 

2450 :class:`~pyTooling.Warning.WarningCollector` collects the 

2451 :exc:`~pyVHDLModel.Exception.DuplicateDeclarationWarning`. 

2452 """ 

2453 self._functions.clear() 

2454 self._procedures.clear() 

2455 self._RemoveDeclaredItemsFromNamespace() 

2456 

2457 for item in self._declaredItems: 

2458 self._IndexDeclaredItem(item) 

2459 

2460 self._areDeclaredItemsIndexed = True 

2461 

2462 def _IndexDeclaredItem(self, item: ModelEntity) -> None: 

2463 """ 

2464 Add a declared item to the namespace and to the lookup table matching its kind. 

2465 

2466 :param item: The declared item. 

2467 """ 

2468 from pyVHDLModel.Subprogram import Function, Procedure 

2469 

2470 if isinstance(item, Function): 

2471 # FIXME: overloads are only appended to a list, not matched/resolved by signature (no 

2472 # real overload resolution yet). 

2473 self._functions.setdefault(item._normalizedIdentifier, []).append(item) 

2474 self._namespace.AddElement(item._normalizedIdentifier, item, overloadable=True) 

2475 elif isinstance(item, Procedure): 2475 ↛ 2481line 2475 didn't jump to line 2481 because the condition on line 2475 was always true

2476 # FIXME: overloads are only appended to a list, not matched/resolved by signature (no 

2477 # real overload resolution yet). 

2478 self._procedures.setdefault(item._normalizedIdentifier, []).append(item) 

2479 self._namespace.AddElement(item._normalizedIdentifier, item, overloadable=True) 

2480 else: 

2481 self._IndexOtherDeclaredItem(item) 

2482 

2483 def _UnindexDeclaredItem(self, item: ModelEntity) -> None: 

2484 """ 

2485 Remove a declared item from the namespace and from the lookup table matching its kind. 

2486 

2487 :param item: The declared item. 

2488 """ 

2489 from pyVHDLModel.Subprogram import Function, Procedure 

2490 

2491 if isinstance(item, (Function, Procedure)): 2491 ↛ 2494line 2491 didn't jump to line 2494 because the condition on line 2491 was always true

2492 self._UnindexOverload(item) 

2493 else: 

2494 self._UnindexOtherDeclaredItem(item)