Coverage for src/pystencilssfg/ir/syntax.py: 94%

112 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-04-04 07:16 +0000

1from __future__ import annotations 

2 

3from enum import Enum, auto 

4from typing import ( 

5 Iterable, 

6 TypeVar, 

7 Generic, 

8) 

9 

10from ..lang import HeaderFile 

11 

12from .entities import ( 

13 SfgNamespace, 

14 SfgKernelHandle, 

15 SfgFunction, 

16 SfgClassMember, 

17 SfgVisibility, 

18 SfgClass, 

19) 

20 

21# ========================================================================================================= 

22# 

23# SYNTACTICAL ELEMENTS 

24# 

25# These classes model *code elements*, which represent the actual syntax objects that populate the output 

26# files, their namespaces and class bodies. 

27# 

28# ========================================================================================================= 

29 

30 

31SourceEntity_T = TypeVar( 

32 "SourceEntity_T", 

33 bound=SfgKernelHandle | SfgFunction | SfgClassMember | SfgClass, 

34 covariant=True, 

35) 

36"""Source entities that may have declarations and definitions.""" 

37 

38 

39class SfgEntityDecl(Generic[SourceEntity_T]): 

40 """Declaration of a function, class, method, or constructor""" 

41 

42 __match_args__ = ("entity",) 

43 

44 def __init__(self, entity: SourceEntity_T) -> None: 

45 self._entity = entity 

46 

47 @property 

48 def entity(self) -> SourceEntity_T: 

49 return self._entity 

50 

51 

52class SfgEntityDef(Generic[SourceEntity_T]): 

53 """Definition of a function, class, method, or constructor""" 

54 

55 __match_args__ = ("entity",) 

56 

57 def __init__(self, entity: SourceEntity_T) -> None: 

58 self._entity = entity 

59 

60 @property 

61 def entity(self) -> SourceEntity_T: 

62 return self._entity 

63 

64 

65SfgClassBodyElement = str | SfgEntityDecl[SfgClassMember] | SfgEntityDef[SfgClassMember] 

66"""Elements that may be placed in the visibility blocks of a class body.""" 

67 

68 

69class SfgVisibilityBlock: 

70 """Visibility-qualified block inside a class definition body. 

71 

72 Visibility blocks host the code elements placed inside a class body: 

73 method and constructor declarations, 

74 in-class method and constructor definitions, 

75 as well as variable declarations and definitions. 

76 

77 Args: 

78 visibility: The visibility qualifier of this block 

79 """ 

80 

81 __match_args__ = ("visibility", "elements") 

82 

83 def __init__(self, visibility: SfgVisibility) -> None: 

84 self._vis = visibility 

85 self._elements: list[SfgClassBodyElement] = [] 

86 self._cls: SfgClass | None = None 

87 

88 @property 

89 def visibility(self) -> SfgVisibility: 

90 return self._vis 

91 

92 @property 

93 def elements(self) -> list[SfgClassBodyElement]: 

94 return self._elements 

95 

96 @elements.setter 

97 def elements(self, elems: Iterable[SfgClassBodyElement]): 

98 self._elements = list(elems) 

99 

100 

101class SfgNamespaceBlock: 

102 """A C++ namespace block. 

103 

104 Args: 

105 namespace: Namespace associated with this block 

106 label: Label printed at the opening brace of this block. 

107 This may be the namespace name, or a compressed qualified 

108 name containing one or more of its parent namespaces. 

109 """ 

110 

111 __match_args__ = ( 

112 "namespace", 

113 "elements", 

114 "label", 

115 ) 

116 

117 def __init__(self, namespace: SfgNamespace, label: str | None = None) -> None: 

118 self._namespace = namespace 

119 self._label = label if label is not None else namespace.name 

120 self._elements: list[SfgNamespaceElement] = [] 

121 

122 @property 

123 def namespace(self) -> SfgNamespace: 

124 return self._namespace 

125 

126 @property 

127 def label(self) -> str: 

128 return self._label 

129 

130 @property 

131 def elements(self) -> list[SfgNamespaceElement]: 

132 """Sequence of source elements that make up the body of this namespace""" 

133 return self._elements 

134 

135 @elements.setter 

136 def elements(self, elems: Iterable[SfgNamespaceElement]): 

137 self._elements = list(elems) 

138 

139 

140class SfgClassBody: 

141 """Body of a class definition.""" 

142 

143 __match_args__ = ("associated_class", "visibility_blocks") 

144 

145 def __init__( 

146 self, 

147 cls: SfgClass, 

148 default_block: SfgVisibilityBlock, 

149 vis_blocks: Iterable[SfgVisibilityBlock], 

150 ) -> None: 

151 self._cls = cls 

152 assert default_block.visibility == SfgVisibility.DEFAULT 

153 self._default_block = default_block 

154 self._blocks = [self._default_block] + list(vis_blocks) 

155 

156 @property 

157 def associated_class(self) -> SfgClass: 

158 return self._cls 

159 

160 @property 

161 def default(self) -> SfgVisibilityBlock: 

162 return self._default_block 

163 

164 def append_visibility_block(self, block: SfgVisibilityBlock): 

165 if block.visibility == SfgVisibility.DEFAULT: 

166 raise ValueError( 

167 "Can't add another block with DEFAULT visibility to this class body." 

168 ) 

169 self._blocks.append(block) 

170 

171 @property 

172 def visibility_blocks(self) -> tuple[SfgVisibilityBlock, ...]: 

173 return tuple(self._blocks) 

174 

175 

176SfgNamespaceElement = ( 

177 str | SfgNamespaceBlock | SfgClassBody | SfgEntityDecl | SfgEntityDef 

178) 

179"""Elements that may be placed inside a namespace, including the global namespace.""" 

180 

181 

182class SfgSourceFileType(Enum): 

183 HEADER = auto() 

184 TRANSLATION_UNIT = auto() 

185 

186 

187class SfgSourceFile: 

188 """A C++ source file. 

189 

190 Args: 

191 name: Name of the file (without parent directories), e.g. ``Algorithms.cpp`` 

192 file_type: Type of the source file (header or translation unit) 

193 prelude: Optionally, text of the prelude comment printed at the top of the file 

194 """ 

195 

196 def __init__( 

197 self, name: str, file_type: SfgSourceFileType, prelude: str | None = None 

198 ) -> None: 

199 self._name: str = name 

200 self._file_type: SfgSourceFileType = file_type 

201 self._prelude: str | None = prelude 

202 self._includes: list[HeaderFile] = [] 

203 self._elements: list[SfgNamespaceElement] = [] 

204 

205 @property 

206 def name(self) -> str: 

207 """Name of this source file""" 

208 return self._name 

209 

210 @property 

211 def file_type(self) -> SfgSourceFileType: 

212 """File type of this source file""" 

213 return self._file_type 

214 

215 @property 

216 def prelude(self) -> str | None: 

217 """Text of the prelude comment""" 

218 return self._prelude 

219 

220 @prelude.setter 

221 def prelude(self, text: str | None): 

222 self._prelude = text 

223 

224 @property 

225 def includes(self) -> list[HeaderFile]: 

226 """Sequence of header files to be included at the top of this file""" 

227 return self._includes 

228 

229 @includes.setter 

230 def includes(self, incl: Iterable[HeaderFile]): 

231 self._includes = list(incl) 

232 

233 @property 

234 def elements(self) -> list[SfgNamespaceElement]: 

235 """Sequence of source elements comprising the body of this file""" 

236 return self._elements 

237 

238 @elements.setter 

239 def elements(self, elems: Iterable[SfgNamespaceElement]): 

240 self._elements = list(elems)