Coverage for pyVHDLModel/Base.py: 72%

178 statements  

« prev     ^ index     » next       coverage.py v7.15.1, created at 2026-07-13 17:58 +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 

35Base-classes for the VHDL language model. 

36""" 

37from enum import unique, Enum 

38from typing import Type, Tuple, Iterable, Optional as Nullable, Union, cast 

39 

40from pyTooling.Decorators import export, readonly 

41from pyTooling.MetaClasses import ExtendedType 

42 

43 

44__all__ = ["ExpressionUnion"] 

45 

46 

47ExpressionUnion = Union[ 

48 'BaseExpression', 

49 'QualifiedExpression', 

50 'FunctionCall', 

51 'TypeConversion', 

52 # ConstantOrSymbol, TODO: ObjectSymbol 

53 'Literal', 

54] 

55 

56 

57@export 

58@unique 

59class Direction(Enum): 

60 """An enumeration representing a direction in a range (``to`` or ``downto``).""" 

61 

62 To = 0 #: Ascending direction 

63 DownTo = 1 #: Descending direction 

64 

65 def __str__(self) -> str: 

66 """ 

67 Formats the direction to ``to`` or ``downto``. 

68 

69 :returns: Formatted direction. 

70 """ 

71 return ("to", "downto")[cast(int, self.value)] # TODO: check performance 

72 

73 

74@export 

75@unique 

76class Mode(Enum): 

77 """ 

78 A ``Mode`` is an enumeration. It represents the direction of data exchange (``in``, ``out``, ...) for objects in 

79 generic, port or parameter lists. 

80 

81 In case no *mode* is defined, ``Default`` is used, so the *mode* is inferred from context. 

82 """ 

83 

84 Default = 0 #: Mode not defined, thus it's context dependent. 

85 In = 1 #: Input 

86 Out = 2 #: Output 

87 InOut = 3 #: Bi-directional 

88 Buffer = 4 #: Buffered output 

89 Linkage = 5 #: undocumented 

90 

91 def __str__(self) -> str: 

92 """ 

93 Formats the direction. 

94 

95 :returns: Formatted direction. 

96 """ 

97 return ("", "in", "out", "inout", "buffer", "linkage")[cast(int, self.value)] # TODO: check performance 

98 

99 

100@export 

101class ModelEntity(metaclass=ExtendedType, slots=True): 

102 """ 

103 ``ModelEntity`` is the base-class for all classes in the VHDL language model, except for mixin classes (see multiple 

104 inheritance) and enumerations. 

105 

106 Each entity in this model has a reference to its parent entity. Therefore, a protected variable :attr:`_parent` is 

107 available and a readonly property :attr:`Parent`. 

108 """ 

109 

110 _parent: 'ModelEntity' #: Reference to a parent entity in the logical model hierarchy. 

111 

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

113 """ 

114 Initializes a VHDL model entity. 

115 

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

117 """ 

118 self._parent = parent 

119 

120 @property 

121 def Parent(self) -> 'ModelEntity': 

122 """ 

123 Property to access the model entity's parent element reference in a logical hierarchy (:attr:`_parent`). 

124 

125 :returns: Reference to the parent entity. 

126 """ 

127 return self._parent 

128 

129 @Parent.setter 

130 def Parent(self, parent: 'ModelEntity') -> None: 

131 if parent is None: 131 ↛ 132line 131 didn't jump to line 132 because the condition on line 131 was never true

132 raise ValueError("Parameter 'parent' is None.") 

133 

134 self._parent = parent 

135 

136 def GetAncestor(self, type: Type) -> 'ModelEntity': 

137 parent = self._parent 

138 while not isinstance(parent, type): 

139 parent = parent._parent 

140 

141 return parent 

142 

143 

144@export 

145class NamedEntityMixin(metaclass=ExtendedType, mixin=True): 

146 """ 

147 A ``NamedEntityMixin`` is a mixin class for all VHDL entities that have an identifier. 

148 

149 Protected variables :attr:`_identifier` and :attr:`_normalizedIdentifier` are available to derived classes as well as 

150 two readonly properties :attr:`Identifier` and :attr:`NormalizedIdentifier` for public access. 

151 """ 

152 

153 _identifier: str #: The identifier of a model entity. 

154 _normalizedIdentifier: str #: The normalized (lower case) identifier of a model entity. 

155 

156 def __init__(self, identifier: str) -> None: 

157 """ 

158 Initializes a named entity. 

159 

160 :param identifier: Identifier (name) of the model entity. 

161 """ 

162 self._identifier = identifier 

163 self._normalizedIdentifier = identifier.lower() 

164 

165 @readonly 

166 def Identifier(self) -> str: 

167 """ 

168 Returns a model entity's identifier (name). 

169 

170 :returns: Name of a model entity. 

171 """ 

172 return self._identifier 

173 

174 @readonly 

175 def NormalizedIdentifier(self) -> str: 

176 """ 

177 Returns a model entity's normalized identifier (lower case name). 

178 

179 :returns: Normalized name of a model entity. 

180 """ 

181 return self._normalizedIdentifier 

182 

183 

184@export 

185class OptionallyNamedEntityMixin(metaclass=ExtendedType, mixin=True): 

186 """ 

187 A ``OptionallyNamedEntityMixin`` is a mixin class for all VHDL entities that have an optional identifier. 

188 

189 Protected variables :attr:`_identifier` and :attr:`_normalizedIdentifier` are available to derived classes as well as 

190 two readonly properties :attr:`Identifier` and :attr:`NormalizedIdentifier` for public access. 

191 """ 

192 

193 _identifier: Nullable[str] #: The identifier of a model entity. 

194 _normalizedIdentifier: Nullable[str] #: The normalized (lower case) identifier of a model entity. 

195 

196 def __init__(self, identifier: Nullable[str]) -> None: 

197 """ 

198 Initializes a named entity. 

199 

200 :param identifier: Identifier (name) of the model entity. 

201 """ 

202 self._identifier = identifier 

203 self._normalizedIdentifier = identifier.lower() if identifier is not None else None 

204 

205 @readonly 

206 def Identifier(self) -> Nullable[str]: 

207 """ 

208 Returns a model entity's identifier (name). 

209 

210 :returns: Name of a model entity. 

211 """ 

212 return self._identifier 

213 

214 @readonly 

215 def NormalizedIdentifier(self) -> Nullable[str]: 

216 """ 

217 Returns a model entity's normalized identifier (lower case name). 

218 

219 :returns: Normalized name of a model entity. 

220 """ 

221 return self._normalizedIdentifier 

222 

223 

224@export 

225class MultipleNamedEntityMixin(metaclass=ExtendedType, mixin=True): 

226 """ 

227 A ``MultipleNamedEntityMixin`` is a mixin class for all VHDL entities that declare multiple instances at once by 

228 defining multiple identifiers. 

229 

230 Protected variables :attr:`_identifiers` and :attr:`_normalizedIdentifiers` are available to derived classes as well 

231 as two readonly properties :attr:`Identifiers` and :attr:`NormalizedIdentifiers` for public access. 

232 """ 

233 

234 _identifiers: Tuple[str] #: A list of identifiers. 

235 _normalizedIdentifiers: Tuple[str] #: A list of normalized (lower case) identifiers. 

236 

237 def __init__(self, identifiers: Iterable[str]) -> None: 

238 """ 

239 Initializes a multiple-named entity. 

240 

241 :param identifiers: Sequence of identifiers (names) of the model entity. 

242 """ 

243 self._identifiers = tuple(identifiers) 

244 self._normalizedIdentifiers = tuple([identifier.lower() for identifier in identifiers]) 

245 

246 @readonly 

247 def Identifiers(self) -> Tuple[str]: 

248 """ 

249 Returns a model entity's tuple of identifiers (names). 

250 

251 :returns: Tuple of identifiers. 

252 """ 

253 return self._identifiers 

254 

255 @readonly 

256 def NormalizedIdentifiers(self) -> Tuple[str]: 

257 """ 

258 Returns a model entity's tuple of normalized identifiers (lower case names). 

259 

260 :returns: Tuple of normalized identifiers. 

261 """ 

262 return self._normalizedIdentifiers 

263 

264 

265@export 

266class LabeledEntityMixin(metaclass=ExtendedType, mixin=True): 

267 """ 

268 A ``LabeledEntityMixin`` is a mixin class for all VHDL entities that can have labels. 

269 

270 protected variables :attr:`_label` and :attr:`_normalizedLabel` are available to derived classes as well as two 

271 readonly properties :attr:`Label` and :attr:`NormalizedLabel` for public access. 

272 """ 

273 _label: Nullable[str] #: The label of a model entity. 

274 _normalizedLabel: Nullable[str] #: The normalized (lower case) label of a model entity. 

275 

276 def __init__(self, label: Nullable[str]) -> None: 

277 """ 

278 Initializes a labeled entity. 

279 

280 :param label: Label of the model entity. 

281 """ 

282 self._label = label 

283 self._normalizedLabel = label.lower() if label is not None else None 

284 

285 @readonly 

286 def Label(self) -> Nullable[str]: 

287 """ 

288 Returns a model entity's label. 

289 

290 :returns: Label of a model entity. 

291 """ 

292 return self._label 

293 

294 @readonly 

295 def NormalizedLabel(self) -> Nullable[str]: 

296 """ 

297 Returns a model entity's normalized (lower case) label. 

298 

299 :returns: Normalized label of a model entity. 

300 """ 

301 return self._normalizedLabel 

302 

303 

304@export 

305class DocumentedEntityMixin(metaclass=ExtendedType, mixin=True): 

306 """ 

307 A ``DocumentedEntityMixin`` is a mixin class for all VHDL entities that can have an associated documentation. 

308 

309 A protected variable :attr:`_documentation` is available to derived classes as well as a readonly property 

310 :attr:`Documentation` for public access. 

311 """ 

312 

313 _documentation: Nullable[str] #: The associated documentation of a model entity. 

314 

315 def __init__(self, documentation: Nullable[str]) -> None: 

316 """ 

317 Initializes a documented entity. 

318 

319 :param documentation: Documentation of a model entity. 

320 """ 

321 self._documentation = documentation 

322 

323 @readonly 

324 def Documentation(self) -> Nullable[str]: 

325 """ 

326 Returns a model entity's associated documentation. 

327 

328 :returns: Associated documentation of a model entity. 

329 """ 

330 return self._documentation 

331 

332 

333@export 

334class ConditionalMixin(metaclass=ExtendedType, mixin=True): 

335 """A ``ConditionalMixin`` is a mixin-class for all statements with a condition.""" 

336 

337 _condition: ExpressionUnion 

338 

339 def __init__(self, condition: Nullable[ExpressionUnion] = None) -> None: 

340 """ 

341 Initializes a statement with a condition. 

342 

343 When the condition is not None, the condition's parent reference is set to this statement. 

344 

345 :param condition: The expression representing the condition. 

346 """ 

347 self._condition = condition 

348 if condition is not None: 

349 condition.Parent = self 

350 

351 @readonly 

352 def Condition(self) -> ExpressionUnion: 

353 """ 

354 Read-only property to access the condition of a statement (:attr:`_condition`). 

355 

356 :returns: The expression representing the condition of a statement. 

357 """ 

358 return self._condition 

359 

360 

361@export 

362class BranchMixin(metaclass=ExtendedType, mixin=True): 

363 """A ``BranchMixin`` is a mixin-class for all statements with branches.""" 

364 

365 def __init__(self) -> None: 

366 pass 

367 

368 

369@export 

370class ConditionalBranchMixin(BranchMixin, ConditionalMixin, mixin=True): 

371 """A ``BaseBranch`` is a mixin-class for all branch statements with a condition.""" 

372 def __init__(self, condition: ExpressionUnion) -> None: 

373 super().__init__() 

374 ConditionalMixin.__init__(self, condition) 

375 

376 

377@export 

378class IfBranchMixin(ConditionalBranchMixin, mixin=True): 

379 """A ``BaseIfBranch`` is a mixin-class for all if-branches.""" 

380 

381 

382@export 

383class ElsifBranchMixin(ConditionalBranchMixin, mixin=True): 

384 """A ``BaseElsifBranch`` is a mixin-class for all elsif-branches.""" 

385 

386 

387@export 

388class ElseBranchMixin(BranchMixin, mixin=True): 

389 """A ``BaseElseBranch`` is a mixin-class for all else-branches.""" 

390 

391 

392@export 

393class ReportStatementMixin(metaclass=ExtendedType, mixin=True): 

394 """A ``MixinReportStatement`` is a mixin-class for all report and assert statements.""" 

395 

396 _message: Nullable[ExpressionUnion] 

397 _severity: Nullable[ExpressionUnion] 

398 

399 def __init__(self, message: Nullable[ExpressionUnion] = None, severity: Nullable[ExpressionUnion] = None) -> None: 

400 self._message = message 

401 if message is not None: 

402 message.Parent = self 

403 

404 self._severity = severity 

405 if severity is not None: 

406 severity.Parent = self 

407 

408 @property 

409 def Message(self) -> Nullable[ExpressionUnion]: 

410 return self._message 

411 

412 @property 

413 def Severity(self) -> Nullable[ExpressionUnion]: 

414 return self._severity 

415 

416 

417@export 

418class AssertStatementMixin(ReportStatementMixin, ConditionalMixin, mixin=True): 

419 """A ``MixinAssertStatement`` is a mixin-class for all assert statements.""" 

420 

421 def __init__(self, condition: ExpressionUnion, message: Nullable[ExpressionUnion] = None, severity: Nullable[ExpressionUnion] = None) -> None: 

422 super().__init__(message, severity) 

423 ConditionalMixin.__init__(self, condition) 

424 

425 

426class BlockStatementMixin(metaclass=ExtendedType, mixin=True): 

427 """A ``BlockStatement`` is a mixin-class for all block statements.""" 

428 

429 def __init__(self) -> None: 

430 pass 

431 

432 

433@export 

434class BaseChoice(ModelEntity): 

435 """A ``Choice`` is a base-class for all choices.""" 

436 

437 

438@export 

439class BaseCase(ModelEntity): 

440 """ 

441 A ``Case`` is a base-class for all cases. 

442 """ 

443 

444 

445@export 

446class Range(ModelEntity): 

447 _leftBound: ExpressionUnion 

448 _rightBound: ExpressionUnion 

449 _direction: Direction 

450 

451 def __init__(self, leftBound: ExpressionUnion, rightBound: ExpressionUnion, direction: Direction, parent: Nullable[ModelEntity] = None) -> None: 

452 super().__init__(parent) 

453 

454 self._leftBound = leftBound 

455 leftBound.Parent = self 

456 

457 self._rightBound = rightBound 

458 rightBound.Parent = self 

459 

460 self._direction = direction 

461 

462 @property 

463 def LeftBound(self) -> ExpressionUnion: 

464 return self._leftBound 

465 

466 @property 

467 def RightBound(self) -> ExpressionUnion: 

468 return self._rightBound 

469 

470 @property 

471 def Direction(self) -> Direction: 

472 return self._direction 

473 

474 def __str__(self) -> str: 

475 return f"{self._leftBound!s} {self._direction!s} {self._rightBound!s}" 

476 

477 

478@export 

479class WaveformElement(ModelEntity): 

480 _expression: ExpressionUnion 

481 _after: ExpressionUnion 

482 

483 def __init__(self, expression: ExpressionUnion, after: Nullable[ExpressionUnion] = None, parent: Nullable[ModelEntity] = None) -> None: 

484 super().__init__(parent) 

485 

486 self._expression = expression 

487 expression.Parent = self 

488 

489 self._after = after 

490 if after is not None: 

491 after.Parent = self 

492 

493 @property 

494 def Expression(self) -> ExpressionUnion: 

495 return self._expression 

496 

497 @property 

498 def After(self) -> Expression: 

499 return self._after