Coverage for pyVHDLModel/Declaration.py: 94%

128 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 

35 

36""" 

37from __future__ import annotations 

38 

39from enum import unique, Enum 

40from typing import TYPE_CHECKING, ClassVar, Iterable, Union, Optional as Nullable 

41 

42from pyTooling.Decorators import export, readonly 

43 

44from pyVHDLModel.Base import ModelEntity, NamedEntityMixin, DocumentedEntityMixin, ReservedWord as _ReservedWord 

45from pyVHDLModel.Expression import BaseExpression, QualifiedExpression, FunctionCall, TypeConversion, Literal 

46from pyVHDLModel.Name import Name 

47from pyVHDLModel.Symbol import Symbol, SubtypeSymbol 

48 

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

50 from pyVHDLModel.Visitor import Visitor 

51 

52 

53 

54ExpressionUnion = Union[ 

55 BaseExpression, 

56 QualifiedExpression, 

57 FunctionCall, 

58 TypeConversion, 

59 # ConstantOrSymbol, TODO: ObjectSymbol 

60 Literal, 

61] 

62 

63 

64@export 

65@unique 

66class EntityClass(Enum): 

67 """An ``EntityClass`` is an enumeration. It represents a VHDL language entity class (``entity``, ``label``, ...).""" 

68 

69 Entity = 0 #: Entity 

70 Architecture = 1 #: Architecture 

71 Configuration = 2 #: Configuration 

72 Procedure = 3 #: Procedure 

73 Function = 4 #: Function 

74 Package = 5 #: Package 

75 Type = 6 #: Type 

76 Subtype = 7 #: Subtype 

77 Constant = 8 #: Constant 

78 Signal = 9 #: Signal 

79 Variable = 10 #: Variable 

80 Component = 11 #: Component 

81 Label = 12 #: Label 

82 Literal = 13 #: Literal 

83 Units = 14 #: Units 

84 Group = 15 #: Group 

85 File = 16 #: File 

86 Property = 17 #: Property 

87 Sequence = 18 #: Sequence 

88 View = 19 #: View 

89 Others = 20 #: Others 

90 

91 __RESERVED_WORDS: ClassVar[dict[int, _ReservedWord]] = { 

92 0: _ReservedWord.Entity, 

93 1: _ReservedWord.Architecture, 

94 2: _ReservedWord.Configuration, 

95 3: _ReservedWord.Procedure, 

96 4: _ReservedWord.Function, 

97 5: _ReservedWord.Package, 

98 6: _ReservedWord.Type, 

99 7: _ReservedWord.Subtype, 

100 8: _ReservedWord.Constant, 

101 9: _ReservedWord.Signal, 

102 10: _ReservedWord.Variable, 

103 11: _ReservedWord.Component, 

104 12: _ReservedWord.Label, 

105 13: _ReservedWord.Literal, 

106 14: _ReservedWord.Units, 

107 15: _ReservedWord.Group, 

108 16: _ReservedWord.File, 

109 17: _ReservedWord.Property, 

110 18: _ReservedWord.Sequence, 

111 19: _ReservedWord.View 

112 } #: The reserved word of each entity class, by value; ``Others`` has none. 

113 

114 @readonly 

115 def ReservedWord(self) -> Nullable[_ReservedWord]: 

116 """ 

117 Read-only property to return the reserved word of the entity class. 

118 

119 :returns: The reserved word, or ``None`` for ``EntityClass.Others``. 

120 """ 

121 return self.__RESERVED_WORDS.get(self.value, None) 

122 

123 

124@export 

125class Attribute(ModelEntity, NamedEntityMixin, DocumentedEntityMixin): 

126 """ 

127 Represents an attribute declaration. 

128 

129 .. admonition:: Example 

130 

131 .. code-block:: VHDL 

132 

133 attribute TotalBits : natural; 

134 """ 

135 

136 _subtype: Symbol #: Reference to the attribute's subtype. 

137 

138 def __init__( 

139 self, 

140 identifier: str, 

141 subtype: Symbol, 

142 documentation: Nullable[str] = None, 

143 parent: Nullable[ModelEntity] = None 

144 ) -> None: 

145 """ 

146 Initializes an attribute declaration. 

147 

148 :param identifier: The identifier of a model entity. 

149 :param subtype: Reference to the attribute's subtype. 

150 :param documentation: The documentation comment associated with this declaration. 

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

152 """ 

153 super().__init__(parent) 

154 NamedEntityMixin.__init__(self, identifier) 

155 DocumentedEntityMixin.__init__(self, documentation) 

156 

157 self._subtype = subtype 

158 subtype.Parent = self 

159 

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

161 """ 

162 Walk this attribute declaration and its subtype with a visitor. 

163 

164 :param visitor: The visitor receiving the events. 

165 """ 

166 visitor.VisitAttribute(self) 

167 self._subtype.Visit(visitor) 

168 visitor.DepartAttribute(self) 

169 

170 @readonly 

171 def Subtype(self) -> None: 

172 """ 

173 Read-only property to access the subtype (:attr:`_subtype`). 

174 

175 :returns: The subtype. 

176 """ 

177 return self._subtype 

178 

179 def __str__(self) -> str: 

180 """ 

181 Formats the attribute declaration. 

182 

183 **Format:** ``attribute myAttribute: bit`` 

184 

185 :returns: Formatted attribute declaration. 

186 """ 

187 return f"attribute {self._identifier}: {self._subtype}" 

188 

189 

190@export 

191class AttributeSpecification(ModelEntity, DocumentedEntityMixin): 

192 """ 

193 Represents an attribute specification. 

194 

195 .. admonition:: Example 

196 

197 .. code-block:: VHDL 

198 

199 attribute TotalBits of BusType : subtype is 32; 

200 """ 

201 

202 _identifiers: list[Name] #: List of all names the attribute is specified for. 

203 _attribute: Name #: Reference to the specified attribute. 

204 _entityClass: EntityClass #: The entity class the named items belong to. 

205 _expression: ExpressionUnion #: The value assigned to the attribute. 

206 

207 def __init__( 

208 self, 

209 identifiers: Iterable[Name], 

210 attribute: Name, 

211 entityClass: EntityClass, 

212 expression: ExpressionUnion, 

213 documentation: Nullable[str] = None, 

214 parent: Nullable[ModelEntity] = None 

215 ) -> None: 

216 """ 

217 Initializes an attribute specification. 

218 

219 :param identifiers: List of all names the attribute is specified for. 

220 :param attribute: Reference to the specified attribute. 

221 :param entityClass: The entity class the named items belong to. 

222 :param expression: The value assigned to the attribute. 

223 :param documentation: The documentation comment associated with this declaration. 

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

225 """ 

226 super().__init__(parent) 

227 DocumentedEntityMixin.__init__(self, documentation) 

228 

229 self._identifiers = identifierList = [] # TODO: convert to dict 

230 for identifier in identifiers: 

231 identifierList.append(identifier) 

232 identifier.Parent = self 

233 

234 self._attribute = attribute 

235 attribute.Parent = self 

236 

237 self._entityClass = entityClass 

238 

239 self._expression = expression 

240 expression.Parent = self 

241 

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

243 """ 

244 Walk this attribute specification, its attribute, its entity specification and its expression with a visitor. 

245 

246 :param visitor: The visitor receiving the events. 

247 """ 

248 visitor.VisitAttributeSpecification(self) 

249 self._attribute.Visit(visitor) 

250 visitor.VisitEntitySpecification(self) 

251 for index, name in enumerate(self._identifiers): 

252 if index > 0: 

253 visitor.VisitComma(self) 

254 

255 name.Visit(visitor) 

256 

257 visitor.DepartEntitySpecification(self) 

258 visitor.VisitIs(self) 

259 self._expression.Visit(visitor) 

260 visitor.DepartAttributeSpecification(self) 

261 

262 @readonly 

263 def Identifiers(self) -> list[Name]: 

264 """ 

265 Read-only property to access the identifiers (:attr:`_identifiers`). 

266 

267 :returns: List of identifiers. 

268 """ 

269 return self._identifiers 

270 

271 @readonly 

272 def Attribute(self) -> Name: 

273 """ 

274 Read-only property to access the attribute (:attr:`_attribute`). 

275 

276 :returns: The attribute. 

277 """ 

278 return self._attribute 

279 

280 @readonly 

281 def EntityClass(self) -> EntityClass: 

282 """ 

283 Read-only property to access the entity class (:attr:`_entityClass`). 

284 

285 :returns: The entity class. 

286 """ 

287 return self._entityClass 

288 

289 @readonly 

290 def Expression(self) -> ExpressionUnion: 

291 """ 

292 Read-only property to access the expression (:attr:`_expression`). 

293 

294 :returns: The expression. 

295 """ 

296 return self._expression 

297 

298 

299# TODO: move somewhere else 

300@export 

301class Alias(ModelEntity, NamedEntityMixin, DocumentedEntityMixin): 

302 """ 

303 Represents an alias declaration. 

304 

305 :attr:`Name` is a :class:`~pyVHDLModel.Symbol.Symbol` - like every other cross-reference in this 

306 model - rather than a bare :class:`~pyVHDLModel.Name.Name`, so it participates in the usual 

307 resolve-later mechanism (:attr:`~pyVHDLModel.Symbol.Symbol.Reference` / 

308 :attr:`~pyVHDLModel.Symbol.Symbol.IsResolved`). Unlike ``PackageReferenceSymbol`` and similar, 

309 there is no single fixed :class:`~pyVHDLModel.Symbol.PossibleReference` value that always fits: an 

310 alias without a subtype indication can refer to almost anything nameable (an object, a type, a 

311 subprogram, a literal, ...), while an alias *with* a subtype indication can - per the LRM - only 

312 ever refer to an object (a constant, variable, signal, or file); the ``possibleReferences`` passed 

313 to the ``Symbol`` should reflect whichever case applies. 

314 

315 .. admonition:: Example 

316 

317 .. code-block:: VHDL 

318 

319 alias a : bit_vector(3 downto 0) is s(3 downto 0); 

320 -- ^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^ 

321 -- optional Subtype Name 

322 

323 alias b is s; 

324 -- ^ 

325 -- Name 

326 """ 

327 

328 _name: Symbol #: Reference to the name being aliased. 

329 _subtype: Nullable[SubtypeSymbol] #: Reference to the alias' subtype, or ``None`` if none was given. 

330 

331 def __init__( 

332 self, 

333 identifier: str, 

334 name: Symbol, 

335 subtype: Nullable[SubtypeSymbol] = None, 

336 documentation: Nullable[str] = None, 

337 parent: Nullable[ModelEntity] = None 

338 ) -> None: 

339 """ 

340 Initializes an alias declaration. 

341 

342 :param identifier: The identifier of a model entity. 

343 :param name: Reference to the name being aliased. 

344 :param subtype: Reference to the alias' subtype, or ``None`` if none was given. 

345 :param documentation: The documentation comment associated with this declaration. 

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

347 """ 

348 super().__init__(parent) 

349 NamedEntityMixin.__init__(self, identifier) 

350 DocumentedEntityMixin.__init__(self, documentation) 

351 

352 self._name = name 

353 name.Parent = self 

354 

355 self._subtype = subtype 

356 if subtype is not None: 

357 subtype.Parent = self 

358 

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

360 """ 

361 Walk this alias declaration, its subtype (if any) and its name with a visitor. 

362 

363 :param visitor: The visitor receiving the events. 

364 """ 

365 visitor.VisitAlias(self) 

366 if self._subtype is not None: 

367 self._subtype.Visit(visitor) 

368 

369 visitor.VisitIs(self) 

370 self._name.Visit(visitor) 

371 visitor.DepartAlias(self) 

372 

373 @readonly 

374 def Name(self) -> Symbol: 

375 """ 

376 Read-only property to access the name (:attr:`_name`). 

377 

378 :returns: The name. 

379 """ 

380 return self._name 

381 

382 @readonly 

383 def Subtype(self) -> Nullable[SubtypeSymbol]: 

384 """ 

385 Read-only property to access the subtype (:attr:`_subtype`). 

386 

387 :returns: The subtype, or ``None`` if not set. 

388 """ 

389 return self._subtype 

390 

391 def __str__(self) -> str: 

392 """ 

393 Formats the alias declaration. 

394 

395 **Format:** ``alias myAlias : bit is target``, or without the subtype when none was given 

396 

397 :returns: Formatted alias declaration. 

398 """ 

399 subtype = f" : {self._subtype}" if self._subtype is not None else "" 

400 return f"alias {self._identifier}{subtype} is {self._name}"