Coverage for pyVHDLModel/Name.py: 96%

153 statements  

« prev     ^ index     » next       coverage.py v7.17.0, created at 2026-10-25 10:05 +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 

35VHDL uses *names* to express cross-references from *usage locations* to *declarations*. Here, *names* are single or 

36combined identifiers. :mod:`Symbols <pyVHDLModel.Symbol>` are structures representing a *name* and a reference 

37(pointer) to the referenced vhdl language entity. 

38""" 

39from __future__ import annotations 

40 

41from typing import TYPE_CHECKING, Iterable, Optional as Nullable, Self 

42 

43from pyTooling.Decorators import export, readonly 

44from pyTooling.MetaClasses import abstractclass 

45 

46from pyVHDLModel.Base import ModelEntity, ExpressionUnion 

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

48 from pyVHDLModel.Visitor import Visitor 

49 

50 

51@export 

52@abstractclass 

53class Name(ModelEntity): 

54 """ 

55 ``Name`` is the base-class for all *names* in the VHDL language model. 

56 

57 .. seealso:: 

58 

59 * :class:`Simple name <pyVHDLModel.Name.SimpleName>` 

60 * :class:`Parenthesis name <pyVHDLModel.Name.ParenthesisName>` 

61 * :class:`Indexed name <pyVHDLModel.Name.IndexedName>` 

62 * :class:`Sliced name <pyVHDLModel.Name.SlicedName>` 

63 * :class:`Selected name <pyVHDLModel.Name.SelectedName>` 

64 * :class:`Attribute name <pyVHDLModel.Name.AttributeName>` 

65 * :class:`Open name <pyVHDLModel.Name.OpenName>` 

66 """ 

67 

68 _identifier: str #: The name's identifier. 

69 _normalizedIdentifier: str #: The normalized (lower case) identifier. 

70 # TODO: seams to be unused. There is no reverse linking, or? 

71 _root: Nullable[Name] #: Reference to the root of the name chain. 

72 _prefix: Nullable[Name] #: Reference to the name's prefix, or ``None`` for a simple name. 

73 

74 def __init__(self, identifier: str, prefix: Nullable[Name] = None, parent: Nullable[ModelEntity] = None) -> None: 

75 """ 

76 Initializes a name. 

77 

78 :param identifier: The name's identifier. 

79 :param prefix: Reference to the name's prefix, or ``None`` for a simple name. 

80 :param parent: The parent model entity of this entity. 

81 """ 

82 super().__init__(parent) 

83 

84 self._identifier = identifier 

85 self._normalizedIdentifier = identifier.lower() 

86 

87 if prefix is None: 

88 self._prefix = None 

89 self._root = self 

90 else: 

91 self._prefix = prefix 

92 self._root = prefix._root 

93 

94 @readonly 

95 def Identifier(self) -> str: 

96 """ 

97 Read-only property to access the identifier this name references (:attr:`_identifier`). 

98 

99 :returns: The referenced identifier. 

100 """ 

101 return self._identifier 

102 

103 @readonly 

104 def NormalizedIdentifier(self) -> str: 

105 """ 

106 Read-only property to access the normalized identifier this name references (:attr:`_normalizedIdentifier`). 

107 

108 :returns: The referenced identifier (normalized). 

109 """ 

110 return self._normalizedIdentifier 

111 

112 @readonly 

113 def Root(self) -> Name: 

114 """ 

115 Read-only property to access the root (left-most) element in a chain of names (:attr:`_root`). 

116 

117 In case the name is a :class:`simple name <SimpleName>`, the root points to the name itself. 

118 

119 :returns: The name's root element. 

120 """ 

121 return self._root 

122 

123 @readonly 

124 def Prefix(self) -> Nullable[Name]: 

125 """ 

126 Read-only property to access the name's prefix in a chain of names (:attr:`_prefix`). 

127 

128 :returns: The name left from current name, if not a simple name, otherwise ``None``. 

129 """ 

130 return self._prefix 

131 

132 @readonly 

133 def HasPrefix(self) -> bool: 

134 """ 

135 Check if the name has a prefix, i.e. :attr:`_prefix` is set. 

136 

137 This is true for all names except :class:`simple names <SimpleName>`. 

138 

139 :returns: ``True``, if the name has a prefix. 

140 """ 

141 return self._prefix is not None 

142 

143 def Copy(self) -> Self: 

144 """ 

145 Create a copy of this name. 

146 

147 The copy has no parent. Its prefix, if any, is a copy of this name's prefix. 

148 

149 :returns: The new name. 

150 """ 

151 prefix = None if self._prefix is None else self._prefix.Copy() 

152 return self.__class__(self._identifier, prefix) 

153 

154 def __repr__(self) -> str: 

155 """ 

156 Formats a representation of the name. 

157 

158 **Format:** ``Name: 'sig'`` 

159 

160 :returns: String representation of the name. 

161 """ 

162 return f"Name: '{self.__str__()}'" 

163 

164 def __str__(self) -> str: 

165 """ 

166 Formats the name. 

167 

168 **Format:** ``sig`` 

169 

170 :returns: Formatted name. 

171 """ 

172 return self._identifier 

173 

174 

175@export 

176class SimpleName(Name): 

177 """ 

178 A *simple name* is a name made from a single word. 

179 

180 For example, the entity name in an architecture declaration is a simple name, while the name of the architecture 

181 itself is an identifier. The simple name references is again an identifier in the entity declaration, thus names 

182 reference other (already) declared language entities. 

183 """ 

184 

185 def Visit(self, visitor: Visitor) -> None: 

186 """ 

187 Walk this simple name with a visitor. 

188 

189 :param visitor: The visitor receiving the events. 

190 """ 

191 visitor.VisitSimpleName(self) 

192 visitor.DepartSimpleName(self) 

193 

194 

195@export 

196class ParenthesisName(Name): 

197 """ 

198 Represents a name followed by a parenthesized association list. 

199 

200 Used where indexing and a function call are indistinguishable before resolution. 

201 """ 

202 _associations: list #: List of all associations in the parenthesis. 

203 

204 def __init__(self, prefix: Name, associations: Iterable, parent: Nullable[ModelEntity] = None) -> None: 

205 """ 

206 Initializes a name followed by a parenthesized association list. 

207 

208 :param prefix: Reference to the name's prefix, or ``None`` for a simple name. 

209 :param associations: List of all associations in the parenthesis. 

210 :param parent: The parent model entity of this entity. 

211 """ 

212 super().__init__("", prefix, parent) 

213 

214 self._associations = associationList = [] 

215 for association in associations: 

216 associationList.append(association) 

217 association.Parent = self 

218 

219 @readonly 

220 def Associations(self) -> list: 

221 """ 

222 Read-only property to access the associations (:attr:`_associations`). 

223 

224 :returns: List of associations. 

225 """ 

226 return self._associations 

227 

228 def Copy(self) -> Self: 

229 """ 

230 Create a copy of this parenthesis name. 

231 

232 The copy has no parent. Its prefix and associations are copies of this name's prefix and associations. 

233 

234 :returns: The new parenthesis name. 

235 """ 

236 return self.__class__(self._prefix.Copy(), [association.Copy() for association in self._associations]) 

237 

238 def Visit(self, visitor: Visitor) -> None: 

239 """ 

240 Walk this parenthesis name, its prefix and its associations with a visitor. 

241 

242 :param visitor: The visitor receiving the events. 

243 """ 

244 visitor.VisitParenthesisName(self) 

245 self._prefix.Visit(visitor) 

246 visitor.VisitLeftParenthesis(self) 

247 for position, association in enumerate(self._associations): 

248 if position > 0: 

249 visitor.VisitComma(self) 

250 

251 association.Visit(visitor) 

252 

253 visitor.DepartParenthesisName(self) 

254 

255 def __str__(self) -> str: 

256 """ 

257 Formats the parenthesis name. 

258 

259 **Format:** ``func(a, b)`` 

260 

261 :returns: Formatted parenthesis name. 

262 """ 

263 return f"{self._prefix!s}({', '.join(str(a) for a in self._associations)})" 

264 

265 

266@export 

267class IndexedName(Name): 

268 """ 

269 Represents a name indexing an array by one or more values. 

270 

271 .. admonition:: Example 

272 

273 .. code-block:: VHDL 

274 

275 s <= v(0); 

276 -- ^^^^ <- the indexed name 

277 """ 

278 _indices: list[ExpressionUnion] #: List of all index expressions, one per dimension. 

279 

280 def __init__(self, prefix: Name, indices: Iterable[ExpressionUnion], parent: Nullable[ModelEntity] = None) -> None: 

281 """ 

282 Initializes a name indexing an array by one or more values. 

283 

284 :param prefix: Reference to the name's prefix, or ``None`` for a simple name. 

285 :param indices: List of all index expressions, one per dimension. 

286 :param parent: The parent model entity of this entity. 

287 """ 

288 super().__init__("", prefix, parent) 

289 

290 self._indices = indexList = [] 

291 for index in indices: 

292 indexList.append(index) 

293 index.Parent = self 

294 

295 @readonly 

296 def Indices(self) -> list[ExpressionUnion]: 

297 """ 

298 Read-only property to access the indices (:attr:`_indices`). 

299 

300 :returns: List of indices. 

301 """ 

302 return self._indices 

303 

304 def Copy(self) -> Self: 

305 """ 

306 Create a copy of this indexed name. 

307 

308 The copy has no parent. Its prefix and indices are copies of this name's prefix and indices. 

309 

310 :returns: The new indexed name. 

311 """ 

312 return self.__class__(self._prefix.Copy(), [index.Copy() for index in self._indices]) 

313 

314 def Visit(self, visitor: Visitor) -> None: 

315 """ 

316 Walk this indexed name, its prefix and its indices with a visitor. 

317 

318 :param visitor: The visitor receiving the events. 

319 """ 

320 visitor.VisitIndexedName(self) 

321 self._prefix.Visit(visitor) 

322 visitor.VisitLeftParenthesis(self) 

323 for position, index in enumerate(self._indices): 

324 if position > 0: 

325 visitor.VisitComma(self) 

326 

327 index.Visit(visitor) 

328 

329 visitor.DepartIndexedName(self) 

330 

331 def __str__(self) -> str: 

332 """ 

333 Formats the indexed name. 

334 

335 **Format:** ``arr(0)`` 

336 

337 :returns: Formatted indexed name. 

338 """ 

339 return f"{self._prefix!s}({', '.join(str(i) for i in self._indices)})" 

340 

341 

342@export 

343class SlicedName(Name): 

344 """ 

345 Represents a name selecting a slice of an array. 

346 

347 .. admonition:: Example 

348 

349 .. code-block:: VHDL 

350 

351 vres := v(3 downto 0); 

352 -- ^^^^^^^^^^^^^ <- the sliced name 

353 """ 

354 def Visit(self, visitor: Visitor) -> None: 

355 """ 

356 Walk this sliced name and its prefix with a visitor. 

357 

358 :param visitor: The visitor receiving the events. 

359 """ 

360 visitor.VisitSlicedName(self) 

361 if self._prefix is not None: 

362 self._prefix.Visit(visitor) 

363 

364 visitor.DepartSlicedName(self) 

365 

366 

367@export 

368class SelectedName(Name): 

369 """ 

370 A *selected name* is a name made from multiple words separated by a dot (``.``). 

371 

372 For example, the library and entity name in a direct entity instantiation is a selected name. Here the entity 

373 identifier is a selected name. The library identifier is a :class:`simple name <SimpleName>`, which is 

374 referenced by the selected name via the :attr:`~pyVHDLModel.Name.Prefix` property. 

375 

376 .. seealso:: 

377 

378 * :class:`All name <pyVHDLModel.Name.AllName>` 

379 """ 

380 

381 def __init__(self, identifier: str, prefix: Name, parent: Nullable[ModelEntity] = None) -> None: 

382 """ 

383 Initializes a selected name. 

384 

385 :param identifier: The name's identifier. 

386 :param prefix: Reference to the name's prefix, or ``None`` for a simple name. 

387 :param parent: The parent model entity of this entity. 

388 """ 

389 super().__init__(identifier, prefix, parent) 

390 

391 def Copy(self) -> Self: 

392 """ 

393 Create a copy of this selected name. 

394 

395 The copy has no parent. Its prefix is a copy of this name's prefix. 

396 

397 :returns: The new selected name. 

398 """ 

399 return self.__class__(self._identifier, self._prefix.Copy()) 

400 

401 def Visit(self, visitor: Visitor) -> None: 

402 """ 

403 Walk this selected name and its prefix with a visitor. 

404 

405 :param visitor: The visitor receiving the events. 

406 """ 

407 visitor.VisitSelectedName(self) 

408 self._prefix.Visit(visitor) 

409 visitor.DepartSelectedName(self) 

410 

411 def __str__(self) -> str: 

412 """ 

413 Formats the selected name. 

414 

415 **Format:** ``rec.elem`` 

416 

417 :returns: Formatted selected name. 

418 """ 

419 return f"{self._prefix!s}.{self._identifier}" 

420 

421 

422@export 

423class AttributeName(Name): 

424 """ 

425 Represents a name selecting an attribute of its prefix. 

426 

427 .. admonition:: Example 

428 

429 .. code-block:: VHDL 

430 

431 for i in v'range loop 

432 -- ^^^^^^^ <- the attribute name 

433 """ 

434 def __init__(self, identifier: str, prefix: Name, parent: Nullable[ModelEntity] = None) -> None: 

435 """ 

436 Initializes a name selecting an attribute of its prefix. 

437 

438 :param identifier: The name's identifier. 

439 :param prefix: Reference to the name's prefix, or ``None`` for a simple name. 

440 :param parent: The parent model entity of this entity. 

441 """ 

442 super().__init__(identifier, prefix, parent) 

443 

444 def Copy(self) -> Self: 

445 """ 

446 Create a copy of this attribute name. 

447 

448 The copy has no parent. Its prefix is a copy of this name's prefix. 

449 

450 :returns: The new attribute name. 

451 """ 

452 return self.__class__(self._identifier, self._prefix.Copy()) 

453 

454 def Visit(self, visitor: Visitor) -> None: 

455 """ 

456 Walk this attribute name and its prefix with a visitor. 

457 

458 :param visitor: The visitor receiving the events. 

459 """ 

460 visitor.VisitAttributeName(self) 

461 self._prefix.Visit(visitor) 

462 visitor.DepartAttributeName(self) 

463 

464 def __str__(self) -> str: 

465 """ 

466 Formats the attribute name. 

467 

468 **Format:** ``v'range`` 

469 

470 :returns: Formatted attribute name. 

471 """ 

472 return f"{self._prefix!s}'{self._identifier}" 

473 

474 

475@export 

476class AllName(SelectedName): 

477 """ 

478 The *all name* represents the reserved word ``all`` used in names. 

479 

480 Most likely this name is used in use-statements. 

481 """ 

482 def __init__(self, prefix: Name, parent: Nullable[ModelEntity] = None) -> None: 

483 """ 

484 Initializes an ``all`` name. 

485 

486 :param prefix: Reference to the name's prefix, or ``None`` for a simple name. 

487 :param parent: The parent model entity of this entity. 

488 """ 

489 super().__init__("all", prefix, parent) # TODO: the case of 'ALL' is not preserved 

490 

491 def Copy(self) -> Self: 

492 """ 

493 Create a copy of this ``all`` name. 

494 

495 The copy has no parent. Its prefix is a copy of this name's prefix. 

496 

497 :returns: The new ``all`` name. 

498 """ 

499 return self.__class__(self._prefix.Copy()) 

500 

501 def Visit(self, visitor: Visitor) -> None: 

502 """ 

503 Walk this ``all`` name and its prefix (if any) with a visitor. 

504 

505 :param visitor: The visitor receiving the events. 

506 """ 

507 visitor.VisitAllName(self) 

508 if self._prefix is not None: 

509 self._prefix.Visit(visitor) 

510 

511 visitor.DepartAllName(self) 

512 

513 

514@export 

515class OpenName(Name): 

516 """ 

517 The *open name* represents the reserved word ``open``. 

518 

519 Most likely this name is used in port associations. 

520 """ 

521 def __init__(self, parent: Nullable[ModelEntity] = None) -> None: 

522 """ 

523 Initializes an ``open`` name. 

524 

525 :param parent: The parent model entity of this entity. 

526 """ 

527 super().__init__("open", parent=parent) # TODO: the case of 'OPEN' is not preserved 

528 

529 def Copy(self) -> Self: 

530 """ 

531 Create a copy of this ``open`` name. 

532 

533 The copy has no parent. 

534 

535 :returns: The new ``open`` name. 

536 """ 

537 return self.__class__() 

538 

539 def Visit(self, visitor: Visitor) -> None: 

540 """ 

541 Walk this ``open`` name with a visitor. 

542 

543 :param visitor: The visitor receiving the events. 

544 """ 

545 visitor.VisitOpenName(self) 

546 visitor.DepartOpenName(self) 

547 

548 def __str__(self) -> str: 

549 """ 

550 Formats the open name. 

551 

552 **Format:** ``open`` 

553 

554 :returns: Formatted open name. 

555 """ 

556 return "open"