Coverage for python / lsst / daf / butler / queries / expressions / parser / treeVisitor.py: 98%
45 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-28 08:36 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-28 08:36 +0000
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 software is dual licensed under the GNU General Public License and also
10# under a 3-clause BSD license. Recipients may choose which of these licenses
11# to use; please see the files gpl-3.0.txt and/or bsd_license.txt,
12# respectively. If you choose the GPL option then the following text applies
13# (but note that there is still no warranty even if you opt for BSD instead):
14#
15# This program is free software: you can redistribute it and/or modify
16# it under the terms of the GNU General Public License as published by
17# the Free Software Foundation, either version 3 of the License, or
18# (at your option) any later version.
19#
20# This program is distributed in the hope that it will be useful,
21# but WITHOUT ANY WARRANTY; without even the implied warranty of
22# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23# GNU General Public License for more details.
24#
25# You should have received a copy of the GNU General Public License
26# along with this program. If not, see <https://www.gnu.org/licenses/>.
28from __future__ import annotations
30__all__ = ["TreeVisitor"]
32from abc import ABC, abstractmethod
33from typing import TYPE_CHECKING, Generic, TypeVar
34from uuid import UUID
36if TYPE_CHECKING:
37 import astropy.time
39 from .exprTree import (
40 BoxNode,
41 CircleNode,
42 GlobNode,
43 Node,
44 PointNode,
45 PolygonNode,
46 RangeLiteral,
47 RegionNode,
48 )
51T = TypeVar("T")
54class TreeVisitor(Generic[T], ABC):
55 """Definition of interface for visitor classes.
57 Visitors and tree node classes implement Visitor pattern for tree
58 traversal. Typical use case is to generate different representation
59 of the tree, e.g. transforming parsed tree into SQLAlchemy clause.
61 All methods of the class can (and most likely should) return the
62 "transformed" value of the visited node. This value will be returned
63 from the `Node.visit` method and it will also be passed as an argument
64 to other methods of the visitor.
65 """
67 @abstractmethod
68 def visitNumericLiteral(self, value: str, node: Node) -> T:
69 """Visit NumericLiteral node.
71 Parameters
72 ----------
73 value : `str`
74 The value associated with the visited node, the value is string,
75 exactly as it appears in the original expression. Depending on
76 use case it may need to be converted to `int` or `float`.
77 node : `Node`
78 Corresponding tree node, mostly useful for diagnostics.
79 """
81 @abstractmethod
82 def visitStringLiteral(self, value: str, node: Node) -> T:
83 """Visit StringLiteral node.
85 Parameters
86 ----------
87 value : `str`
88 The value associated with the visited node.
89 node : `Node`
90 Corresponding tree node, mostly useful for diagnostics.
91 """
93 @abstractmethod
94 def visitUuidLiteral(self, value: UUID, node: Node) -> T:
95 """Visit UuidLiteral node.
97 Parameters
98 ----------
99 value : `UUID`
100 The value associated with the visited node, the value is UUID.
101 node : `Node`
102 Corresponding tree node, mostly useful for diagnostics.
103 """
105 @abstractmethod
106 def visitTimeLiteral(self, value: astropy.time.Time, node: Node) -> T:
107 """Visit TimeLiteral node.
109 Parameters
110 ----------
111 value : `astropy.time.Time`
112 The value associated with the visited node.
113 node : `Node`
114 Corresponding tree node, mostly useful for diagnostics.
115 """
117 @abstractmethod
118 def visitRangeLiteral(self, start: int, stop: int, stride: int | None, node: RangeLiteral) -> T:
119 """Visit RangeLiteral node.
121 Parameters
122 ----------
123 start : `int`
124 Range starting value.
125 stop : `int`
126 Range final value.
127 stride : `int` or `None`
128 Stride, can be `None` if not specified (should be treated same
129 as 1).
130 node : `Node`
131 Corresponding tree node, mostly useful for diagnostics.
132 """
134 @abstractmethod
135 def visitIdentifier(self, name: str, node: Node) -> T:
136 """Visit Identifier node.
138 Parameters
139 ----------
140 name : `str`
141 Identifier name.
142 node : `Node`
143 Corresponding tree node, mostly useful for diagnostics.
144 """
146 @abstractmethod
147 def visitBind(self, name: str, node: Node) -> T:
148 """Visit BindName node.
150 Parameters
151 ----------
152 name : `str`
153 Bind name.
154 node : `Node`
155 Corresponding tree node, mostly useful for diagnostics.
156 """
158 @abstractmethod
159 def visitUnaryOp(self, operator: str, operand: T, node: Node) -> T:
160 """Visit UnaryOp node.
162 Parameters
163 ----------
164 operator : `str`
165 Operator name, e.g. "NOT" or "+".
166 operand : `object`
167 Operand, this object is returned by one of the methods of this
168 class as a result of transformation of some other tree node.
169 node : `Node`
170 Corresponding tree node, mostly useful for diagnostics.
171 """
173 @abstractmethod
174 def visitBinaryOp(self, operator: str, lhs: T, rhs: T, node: Node) -> T:
175 """Visit BinaryOp node.
177 Parameters
178 ----------
179 operator : `str`
180 Operator name, e.g. "NOT" or "+".
181 lhs : `object`
182 Left hand side operand, this object is returned by one of the
183 methods of this class as a result of transformation of some other
184 tree node.
185 rhs : `object`
186 Right hand side operand, this object is returned by one of the
187 methods of this class as a result of transformation of some other
188 tree node.
189 node : `Node`
190 Corresponding tree node, mostly useful for diagnostics.
191 """
193 @abstractmethod
194 def visitIsIn(self, lhs: T, values: list[T], not_in: bool, node: Node) -> T:
195 """Visit IsIn node.
197 Parameters
198 ----------
199 lhs : `object`
200 Left hand side operand, this object is returned by one of the
201 methods of this class as a result of transformation of some other
202 tree node.
203 values : `list` of `object`
204 Right hand side operand, list of objects returned by methods of
205 this class as a result of transformation of some other tree nodes.
206 not_in : `bool`
207 `True` for "NOT IN" expression.
208 node : `Node`
209 Corresponding tree node, mostly useful for diagnostics.
210 """
212 @abstractmethod
213 def visitParens(self, expression: T, node: Node) -> T:
214 """Visit Parens node.
216 Parameters
217 ----------
218 expression : `object`
219 Expression inside parentheses, this object is returned by one of
220 the methods of this class as a result of transformation of some
221 other tree node.
222 node : `Node`
223 Corresponding tree node, mostly useful for diagnostics.
224 """
226 @abstractmethod
227 def visitTupleNode(self, items: tuple[T, ...], node: Node) -> T:
228 """Visit TupleNode node.
230 Parameters
231 ----------
232 items : `tuple` of `object`
233 Expressions inside parentheses, tuple of objects returned by one
234 of the methods of this class as a result of transformation of
235 tuple items.
236 node : `Node`
237 Corresponding tree node, mostly useful for diagnostics.
238 """
240 def visitFunctionCall(self, name: str, args: list[T], node: Node) -> T:
241 """Visit FunctionCall node.
243 Parameters
244 ----------
245 name : `str`
246 Name of the function.
247 args : `list` of `object`
248 Arguments to function, list of objects returned by methods of
249 this class as a result of transformation of function arguments.
250 node : `Node`
251 Corresponding tree node, mostly useful for diagnostics.
253 Notes
254 -----
255 For now we only have to support a small set of functions, and for those
256 functions we define special node types (e.g. `PointNode`, `GlobNode`)
257 and special visit methods. `FunctionCall` node type represents a
258 generic function and regular visitors do not handle generic function.
259 This non-abstract method is a common implementation for those visitors
260 which raises an exception.
261 """
262 raise ValueError(f"Unknown function '{name}' in expression")
264 @abstractmethod
265 def visitPointNode(self, ra: T, dec: T, node: PointNode) -> T:
266 """Visit PointNode node.
268 Parameters
269 ----------
270 ra, dec : `object`
271 Representation of 'ra' and 'dec' values, objects returned by
272 methods of this class as a result of transformation of function
273 arguments.
274 node : `PointNode`
275 Corresponding tree node, mostly useful for diagnostics.
276 """
278 @abstractmethod
279 def visitCircleNode(self, ra: T, dec: T, radius: T, node: CircleNode) -> T:
280 """Visit CircleNode node.
282 Parameters
283 ----------
284 ra, dec, radius : `object`
285 Representation of 'ra', 'dec', and 'radius' values, objects
286 returned by methods of this class as a result of transformation of
287 function arguments.
288 node : `CircleNode`
289 Corresponding tree node, mostly useful for diagnostics.
290 """
292 @abstractmethod
293 def visitBoxNode(self, ra: T, dec: T, width: T, height: T, node: BoxNode) -> T:
294 """Visit BoxNode node.
296 Parameters
297 ----------
298 ra, dec, width, height : `object`
299 Representation of BoxNode parameters values, objects returned by
300 methods of this class as a result of transformation of function
301 arguments.
302 node : `BoxNode`
303 Corresponding tree node, mostly useful for diagnostics.
304 """
306 @abstractmethod
307 def visitPolygonNode(self, vertices: list[tuple[T, T]], node: PolygonNode) -> T:
308 """Visit BoxNode node.
310 Parameters
311 ----------
312 vertices : `list` [`tuple`]
313 Representation of BoxNode vertices, objects returned by
314 methods of this class as a result of transformation of function
315 arguments.
316 node : `PolygonNode`
317 Corresponding tree node, mostly useful for diagnostics.
318 """
320 @abstractmethod
321 def visitRegionNode(self, pos: T, node: RegionNode) -> T:
322 """Visit RegionNode node.
324 Parameters
325 ----------
326 pos : `object`
327 Representation of RegionNode argument, object returned by
328 methods of this class as a result of transformation of function
329 arguments.
330 node : `RegionNode`
331 Corresponding tree node, mostly useful for diagnostics.
332 """
334 @abstractmethod
335 def visitGlobNode(self, expression: T, pattern: T, node: GlobNode) -> T:
336 """Visit GlobNode node.
338 Parameters
339 ----------
340 expression, pattern : `object`
341 Representation of 'pattern' and 'expression' values, objects
342 returned by methods of this class as a result of transformation of
343 function arguments.
344 node : `GlobNode`
345 Corresponding tree node, mostly useful for diagnostics.
346 """