Coverage for python/lsst/daf/butler/registry/queries/exprParser/exprTree.py : 41%

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/>.
22"""Module which defines classes for intermediate representation of the
23expression tree produced by parser.
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"""
32__all__ = ['Node', 'BinaryOp', 'Identifier', 'IsIn', 'NumericLiteral',
33 'Parens', 'RangeLiteral', 'StringLiteral', 'TimeLiteral',
34 'UnaryOp']
36# -------------------------------
37# Imports of standard modules --
38# -------------------------------
39from abc import ABC, abstractmethod
41# -----------------------------
42# Imports for other modules --
43# -----------------------------
45# ----------------------------------
46# Local non-exported definitions --
47# ----------------------------------
49# ------------------------
50# Exported definitions --
51# ------------------------
54class Node(ABC):
55 """Base class of IR node in expression tree.
57 The purpose of this class is to simplify visiting of the
58 all nodes in a tree. It has a list of sub-nodes of this
59 node so that visiting code can navigate whole tree without
60 knowing exact types of each node.
62 Attributes
63 ----------
64 children : tuple of :py:class:`Node`
65 Possibly empty list of sub-nodes.
66 """
67 def __init__(self, children=None):
68 self.children = tuple(children or ())
70 @abstractmethod
71 def visit(self, visitor):
72 """Implement Visitor pattern for parsed tree.
74 Parameters
75 ----------
76 visitor : `TreeVisitor`
77 Instance of vistor type.
78 """
81class BinaryOp(Node):
82 """Node representing binary operator.
84 This class is used for representing all binary operators including
85 arithmetic and boolean operations.
87 Attributes
88 ----------
89 lhs : Node
90 Left-hand side of the operation
91 rhs : Node
92 Right-hand side of the operation
93 op : str
94 Operator name, e.g. '+', 'OR'
95 """
96 def __init__(self, lhs, op, rhs):
97 Node.__init__(self, (lhs, rhs))
98 self.lhs = lhs
99 self.op = op
100 self.rhs = rhs
102 def visit(self, visitor):
103 # Docstring inherited from Node.visit
104 lhs = self.lhs.visit(visitor)
105 rhs = self.rhs.visit(visitor)
106 return visitor.visitBinaryOp(self.op, lhs, rhs, self)
108 def __str__(self):
109 return "{lhs} {op} {rhs}".format(**vars(self))
112class UnaryOp(Node):
113 """Node representing unary operator.
115 This class is used for representing all unary operators including
116 arithmetic and boolean operations.
118 Attributes
119 ----------
120 op : str
121 Operator name, e.g. '+', 'NOT'
122 operand : Node
123 Operand.
124 """
125 def __init__(self, op, operand):
126 Node.__init__(self, (operand,))
127 self.op = op
128 self.operand = operand
130 def visit(self, visitor):
131 # Docstring inherited from Node.visit
132 operand = self.operand.visit(visitor)
133 return visitor.visitUnaryOp(self.op, operand, self)
135 def __str__(self):
136 return "{op} {operand}".format(**vars(self))
139class StringLiteral(Node):
140 """Node representing string literal.
142 Attributes
143 ----------
144 value : str
145 Literal value.
146 """
147 def __init__(self, value):
148 Node.__init__(self)
149 self.value = value
151 def visit(self, visitor):
152 # Docstring inherited from Node.visit
153 return visitor.visitStringLiteral(self.value, self)
155 def __str__(self):
156 return "'{value}'".format(**vars(self))
159class TimeLiteral(Node):
160 """Node representing time literal.
162 Attributes
163 ----------
164 value : str
165 Literal string value.
166 """
167 def __init__(self, value):
168 Node.__init__(self)
169 self.value = value
171 def visit(self, visitor):
172 # Docstring inherited from Node.visit
173 return visitor.visitTimeLiteral(self.value, self)
175 def __str__(self):
176 return "'{value}'".format(**vars(self))
179class NumericLiteral(Node):
180 """Node representing string literal.
182 We do not convert literals to numbers, their text representation
183 is stored literally.
185 Attributes
186 ----------
187 value : str
188 Literal value.
189 """
190 def __init__(self, value):
191 Node.__init__(self)
192 self.value = value
194 def visit(self, visitor):
195 # Docstring inherited from Node.visit
196 return visitor.visitNumericLiteral(self.value, self)
198 def __str__(self):
199 return "{value}".format(**vars(self))
202class Identifier(Node):
203 """Node representing identifier.
205 Value of the identifier is its name, it may contain zero or one dot
206 character.
208 Attributes
209 ----------
210 name : str
211 Identifier name.
212 """
213 def __init__(self, name):
214 Node.__init__(self)
215 self.name = name
217 def visit(self, visitor):
218 # Docstring inherited from Node.visit
219 return visitor.visitIdentifier(self.name, self)
221 def __str__(self):
222 return "{name}".format(**vars(self))
225class RangeLiteral(Node):
226 """Node representing range literal appearing in `IN` list.
228 Range literal defines a range of integer numbers with start and
229 end of the range (with inclusive end) and optional stride value
230 (default is 1).
232 Attributes
233 ----------
234 start : `int`
235 Start value of a range.
236 stop : `int`
237 End value of a range, inclusive, same or higher than ``start``.
238 stride : `int` or `None`, optional
239 Stride value, must be positive, can be `None` which means that stride
240 was not specified. Consumers are supposed to treat `None` the same way
241 as stride=1 but for some consumers it may be useful to know that
242 stride was missing from literal.
243 """
244 def __init__(self, start, stop, stride=None):
245 self.start = start
246 self.stop = stop
247 self.stride = stride
249 def visit(self, visitor):
250 # Docstring inherited from Node.visit
251 return visitor.visitRangeLiteral(self.start, self.stop, self.stride, self)
253 def __str__(self):
254 res = f"{self.start}..{self.stop}" + (f":{self.stride}" if self.stride else "")
255 return res
258class IsIn(Node):
259 """Node representing IN or NOT IN expression.
261 Attributes
262 ----------
263 lhs : Node
264 Left-hand side of the operation
265 values : list of Node
266 List of values on the right side.
267 not_in : bool
268 If `True` then it is NOT IN expression, otherwise it is IN expression.
269 """
270 def __init__(self, lhs, values, not_in=False):
271 Node.__init__(self, (lhs,) + tuple(values))
272 self.lhs = lhs
273 self.values = values
274 self.not_in = not_in
276 def visit(self, visitor):
277 # Docstring inherited from Node.visit
278 lhs = self.lhs.visit(visitor)
279 values = [value.visit(visitor) for value in self.values]
280 return visitor.visitIsIn(lhs, values, self.not_in, self)
282 def __str__(self):
283 values = ", ".join(str(x) for x in self.values)
284 not_in = ""
285 if self.not_in:
286 not_in = "NOT "
287 return "{lhs} {not_in}IN ({values})".format(lhs=self.lhs,
288 not_in=not_in,
289 values=values)
292class Parens(Node):
293 """Node representing parenthesized expression.
295 Attributes
296 ----------
297 expr : Node
298 Expression inside parentheses.
299 """
300 def __init__(self, expr):
301 Node.__init__(self, (expr,))
302 self.expr = expr
304 def visit(self, visitor):
305 # Docstring inherited from Node.visit
306 expr = self.expr.visit(visitor)
307 return visitor.visitParens(expr, self)
309 def __str__(self):
310 return "({expr})".format(**vars(self))