Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# This file is part of daf_butler. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (https://www.lsst.org). 

6# See the COPYRIGHT file at the top-level directory of this distribution 

7# for details of code ownership. 

8# 

9# This program is free software: you can redistribute it and/or modify 

10# it under the terms of the GNU General Public License as published by 

11# the Free Software Foundation, either version 3 of the License, or 

12# (at your option) any later version. 

13# 

14# This program is distributed in the hope that it will be useful, 

15# but WITHOUT ANY WARRANTY; without even the implied warranty of 

16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

17# GNU General Public License for more details. 

18# 

19# You should have received a copy of the GNU General Public License 

20# along with this program. If not, see <https://www.gnu.org/licenses/>. 

21 

22"""Module which defines classes for intermediate representation of the 

23expression tree produced by parser. 

24 

25The purpose of the intermediate representation is to be able to generate 

26same expression as a part of SQL statement with the minimal changes. We 

27will need to be able to replace identifiers in original expression with 

28database-specific identifiers but everything else will probably be sent 

29to database directly. 

30""" 

31 

32__all__ = ['Node', 'BinaryOp', 'Identifier', 'IsIn', 'NumericLiteral', 

33 'Parens', 'StringLiteral', 'UnaryOp'] 

34 

35# ------------------------------- 

36# Imports of standard modules -- 

37# ------------------------------- 

38from abc import ABC, abstractmethod 

39 

40# ----------------------------- 

41# Imports for other modules -- 

42# ----------------------------- 

43 

44# ---------------------------------- 

45# Local non-exported definitions -- 

46# ---------------------------------- 

47 

48# ------------------------ 

49# Exported definitions -- 

50# ------------------------ 

51 

52 

53class Node(ABC): 

54 """Base class of IR node in expression tree. 

55 

56 The purpose of this class is to simplify visiting of the 

57 all nodes in a tree. It has a list of sub-nodes of this 

58 node so that visiting code can navigate whole tree without 

59 knowing exact types of each node. 

60 

61 Attributes 

62 ---------- 

63 children : tuple of :py:class:`Node` 

64 Possibly empty list of sub-nodes. 

65 """ 

66 def __init__(self, children=None): 

67 self.children = tuple(children or ()) 

68 

69 @abstractmethod 

70 def visit(self, visitor): 

71 """Implement Visitor pattern for parsed tree. 

72 

73 Parameters 

74 ---------- 

75 visitor : `TreeVisitor` 

76 Instance of vistor type. 

77 """ 

78 

79 

80class BinaryOp(Node): 

81 """Node representing binary operator. 

82 

83 This class is used for representing all binary operators including 

84 arithmetic and boolean operations. 

85 

86 Attributes 

87 ---------- 

88 lhs : Node 

89 Left-hand side of the operation 

90 rhs : Node 

91 Right-hand side of the operation 

92 op : str 

93 Operator name, e.g. '+', 'OR' 

94 """ 

95 def __init__(self, lhs, op, rhs): 

96 Node.__init__(self, (lhs, rhs)) 

97 self.lhs = lhs 

98 self.op = op 

99 self.rhs = rhs 

100 

101 def visit(self, visitor): 

102 # Docstring inherited from Node.visit 

103 lhs = self.lhs.visit(visitor) 

104 rhs = self.rhs.visit(visitor) 

105 return visitor.visitBinaryOp(self.op, lhs, rhs, self) 

106 

107 def __str__(self): 

108 return "{lhs} {op} {rhs}".format(**vars(self)) 

109 

110 

111class UnaryOp(Node): 

112 """Node representing unary operator. 

113 

114 This class is used for representing all unary operators including 

115 arithmetic and boolean operations. 

116 

117 Attributes 

118 ---------- 

119 op : str 

120 Operator name, e.g. '+', 'NOT' 

121 operand : Node 

122 Operand. 

123 """ 

124 def __init__(self, op, operand): 

125 Node.__init__(self, (operand,)) 

126 self.op = op 

127 self.operand = operand 

128 

129 def visit(self, visitor): 

130 # Docstring inherited from Node.visit 

131 operand = self.operand.visit(visitor) 

132 return visitor.visitUnaryOp(self.op, operand, self) 

133 

134 def __str__(self): 

135 return "{op} {operand}".format(**vars(self)) 

136 

137 

138class StringLiteral(Node): 

139 """Node representing string literal. 

140 

141 Attributes 

142 ---------- 

143 value : str 

144 Literal value. 

145 """ 

146 def __init__(self, value): 

147 Node.__init__(self) 

148 self.value = value 

149 

150 def visit(self, visitor): 

151 # Docstring inherited from Node.visit 

152 return visitor.visitStringLiteral(self.value, self) 

153 

154 def __str__(self): 

155 return "'{value}'".format(**vars(self)) 

156 

157 

158class NumericLiteral(Node): 

159 """Node representing string literal. 

160 

161 We do not convert literals to numbers, their text representation 

162 is stored literally. 

163 

164 Attributes 

165 ---------- 

166 value : str 

167 Literal value. 

168 """ 

169 def __init__(self, value): 

170 Node.__init__(self) 

171 self.value = value 

172 

173 def visit(self, visitor): 

174 # Docstring inherited from Node.visit 

175 return visitor.visitNumericLiteral(self.value, self) 

176 

177 def __str__(self): 

178 return "{value}".format(**vars(self)) 

179 

180 

181class Identifier(Node): 

182 """Node representing identifier. 

183 

184 Value of the identifier is its name, it may contain zero or one dot 

185 character. 

186 

187 Attributes 

188 ---------- 

189 name : str 

190 Identifier name. 

191 """ 

192 def __init__(self, name): 

193 Node.__init__(self) 

194 self.name = name 

195 

196 def visit(self, visitor): 

197 # Docstring inherited from Node.visit 

198 return visitor.visitIdentifier(self.name, self) 

199 

200 def __str__(self): 

201 return "{name}".format(**vars(self)) 

202 

203 

204class RangeLiteral(Node): 

205 """Node representing range literal appearing in `IN` list. 

206 

207 Range literal defines a range of integer numbers with start and 

208 end of the range (with inclusive end) and optional stride value 

209 (default is 1). 

210 

211 Attributes 

212 ---------- 

213 start : `int` 

214 Start value of a range. 

215 stop : `int` 

216 End value of a range, inclusive, same or higher than ``start``. 

217 stride : `int` or `None`, optional 

218 Stride value, must be positive, can be `None` which means that stride 

219 was not specified. Consumers are supposed to treat `None` the same way 

220 as stride=1 but for some consumers it may be useful to know that 

221 stride was missing from literal. 

222 """ 

223 def __init__(self, start, stop, stride=None): 

224 self.start = start 

225 self.stop = stop 

226 self.stride = stride 

227 

228 def visit(self, visitor): 

229 # Docstring inherited from Node.visit 

230 return visitor.visitRangeLiteral(self.start, self.stop, self.stride, self) 

231 

232 def __str__(self): 

233 res = f"{self.start}..{self.stop}" + (f":{self.stride}" if self.stride else "") 

234 return res 

235 

236 

237class IsIn(Node): 

238 """Node representing IN or NOT IN expression. 

239 

240 Attributes 

241 ---------- 

242 lhs : Node 

243 Left-hand side of the operation 

244 values : list of Node 

245 List of values on the right side. 

246 not_in : bool 

247 If `True` then it is NOT IN expression, otherwise it is IN expression. 

248 """ 

249 def __init__(self, lhs, values, not_in=False): 

250 Node.__init__(self, (lhs,) + tuple(values)) 

251 self.lhs = lhs 

252 self.values = values 

253 self.not_in = not_in 

254 

255 def visit(self, visitor): 

256 # Docstring inherited from Node.visit 

257 lhs = self.lhs.visit(visitor) 

258 values = [value.visit(visitor) for value in self.values] 

259 return visitor.visitIsIn(lhs, values, self.not_in, self) 

260 

261 def __str__(self): 

262 values = ", ".join(str(x) for x in self.values) 

263 not_in = "" 

264 if self.not_in: 

265 not_in = "NOT " 

266 return "{lhs} {not_in}IN ({values})".format(lhs=self.lhs, 

267 not_in=not_in, 

268 values=values) 

269 

270 

271class Parens(Node): 

272 """Node representing parenthesized expression. 

273 

274 Attributes 

275 ---------- 

276 expr : Node 

277 Expression inside parentheses. 

278 """ 

279 def __init__(self, expr): 

280 Node.__init__(self, (expr,)) 

281 self.expr = expr 

282 

283 def visit(self, visitor): 

284 # Docstring inherited from Node.visit 

285 expr = self.expr.visit(visitor) 

286 return visitor.visitParens(expr, self) 

287 

288 def __str__(self): 

289 return "({expr})".format(**vars(self))