relationalai 1.0.0a3__py3-none-any.whl → 1.0.0a5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- relationalai/config/config.py +47 -21
- relationalai/config/connections/__init__.py +5 -2
- relationalai/config/connections/duckdb.py +2 -2
- relationalai/config/connections/local.py +31 -0
- relationalai/config/connections/snowflake.py +0 -1
- relationalai/config/external/raiconfig_converter.py +235 -0
- relationalai/config/external/raiconfig_models.py +202 -0
- relationalai/config/external/utils.py +31 -0
- relationalai/config/shims.py +1 -0
- relationalai/semantics/__init__.py +10 -8
- relationalai/semantics/backends/sql/sql_compiler.py +1 -4
- relationalai/semantics/experimental/__init__.py +0 -0
- relationalai/semantics/experimental/builder.py +295 -0
- relationalai/semantics/experimental/builtins.py +154 -0
- relationalai/semantics/frontend/base.py +67 -42
- relationalai/semantics/frontend/core.py +34 -6
- relationalai/semantics/frontend/front_compiler.py +209 -37
- relationalai/semantics/frontend/pprint.py +6 -2
- relationalai/semantics/metamodel/__init__.py +7 -0
- relationalai/semantics/metamodel/metamodel.py +2 -0
- relationalai/semantics/metamodel/metamodel_analyzer.py +58 -16
- relationalai/semantics/metamodel/pprint.py +6 -1
- relationalai/semantics/metamodel/rewriter.py +11 -7
- relationalai/semantics/metamodel/typer.py +116 -41
- relationalai/semantics/reasoners/__init__.py +11 -0
- relationalai/semantics/reasoners/graph/__init__.py +35 -0
- relationalai/semantics/reasoners/graph/core.py +9028 -0
- relationalai/semantics/std/__init__.py +30 -10
- relationalai/semantics/std/aggregates.py +641 -12
- relationalai/semantics/std/common.py +146 -13
- relationalai/semantics/std/constraints.py +71 -1
- relationalai/semantics/std/datetime.py +904 -21
- relationalai/semantics/std/decimals.py +143 -2
- relationalai/semantics/std/floats.py +57 -4
- relationalai/semantics/std/integers.py +98 -4
- relationalai/semantics/std/math.py +857 -35
- relationalai/semantics/std/numbers.py +216 -20
- relationalai/semantics/std/re.py +213 -5
- relationalai/semantics/std/strings.py +437 -44
- relationalai/shims/executor.py +60 -52
- relationalai/shims/fixtures.py +85 -0
- relationalai/shims/helpers.py +26 -2
- relationalai/shims/hoister.py +28 -9
- relationalai/shims/mm2v0.py +204 -173
- relationalai/tools/cli/cli.py +192 -10
- relationalai/tools/cli/components/progress_reader.py +1 -1
- relationalai/tools/cli/docs.py +394 -0
- relationalai/tools/debugger.py +11 -4
- relationalai/tools/qb_debugger.py +435 -0
- relationalai/tools/typer_debugger.py +1 -2
- relationalai/util/dataclasses.py +3 -5
- relationalai/util/docutils.py +1 -2
- relationalai/util/error.py +2 -5
- relationalai/util/python.py +23 -0
- relationalai/util/runtime.py +1 -2
- relationalai/util/schema.py +2 -4
- relationalai/util/structures.py +4 -2
- relationalai/util/tracing.py +8 -2
- {relationalai-1.0.0a3.dist-info → relationalai-1.0.0a5.dist-info}/METADATA +8 -5
- {relationalai-1.0.0a3.dist-info → relationalai-1.0.0a5.dist-info}/RECORD +118 -95
- {relationalai-1.0.0a3.dist-info → relationalai-1.0.0a5.dist-info}/WHEEL +1 -1
- v0/relationalai/__init__.py +1 -1
- v0/relationalai/clients/client.py +52 -18
- v0/relationalai/clients/exec_txn_poller.py +122 -0
- v0/relationalai/clients/local.py +23 -8
- v0/relationalai/clients/resources/azure/azure.py +36 -11
- v0/relationalai/clients/resources/snowflake/__init__.py +4 -4
- v0/relationalai/clients/resources/snowflake/cli_resources.py +12 -1
- v0/relationalai/clients/resources/snowflake/direct_access_resources.py +124 -100
- v0/relationalai/clients/resources/snowflake/engine_service.py +381 -0
- v0/relationalai/clients/resources/snowflake/engine_state_handlers.py +35 -29
- v0/relationalai/clients/resources/snowflake/error_handlers.py +43 -2
- v0/relationalai/clients/resources/snowflake/snowflake.py +277 -179
- v0/relationalai/clients/resources/snowflake/use_index_poller.py +8 -0
- v0/relationalai/clients/types.py +5 -0
- v0/relationalai/errors.py +19 -1
- v0/relationalai/semantics/lqp/algorithms.py +173 -0
- v0/relationalai/semantics/lqp/builtins.py +199 -2
- v0/relationalai/semantics/lqp/executor.py +68 -37
- v0/relationalai/semantics/lqp/ir.py +28 -2
- v0/relationalai/semantics/lqp/model2lqp.py +215 -45
- v0/relationalai/semantics/lqp/passes.py +13 -658
- v0/relationalai/semantics/lqp/rewrite/__init__.py +12 -0
- v0/relationalai/semantics/lqp/rewrite/algorithm.py +385 -0
- v0/relationalai/semantics/lqp/rewrite/constants_to_vars.py +70 -0
- v0/relationalai/semantics/lqp/rewrite/deduplicate_vars.py +104 -0
- v0/relationalai/semantics/lqp/rewrite/eliminate_data.py +108 -0
- v0/relationalai/semantics/lqp/rewrite/extract_keys.py +25 -3
- v0/relationalai/semantics/lqp/rewrite/period_math.py +77 -0
- v0/relationalai/semantics/lqp/rewrite/quantify_vars.py +65 -31
- v0/relationalai/semantics/lqp/rewrite/unify_definitions.py +317 -0
- v0/relationalai/semantics/lqp/utils.py +11 -1
- v0/relationalai/semantics/lqp/validators.py +14 -1
- v0/relationalai/semantics/metamodel/builtins.py +2 -1
- v0/relationalai/semantics/metamodel/compiler.py +2 -1
- v0/relationalai/semantics/metamodel/dependency.py +12 -3
- v0/relationalai/semantics/metamodel/executor.py +11 -1
- v0/relationalai/semantics/metamodel/factory.py +2 -2
- v0/relationalai/semantics/metamodel/helpers.py +7 -0
- v0/relationalai/semantics/metamodel/ir.py +3 -2
- v0/relationalai/semantics/metamodel/rewrite/dnf_union_splitter.py +30 -20
- v0/relationalai/semantics/metamodel/rewrite/flatten.py +50 -13
- v0/relationalai/semantics/metamodel/rewrite/format_outputs.py +9 -3
- v0/relationalai/semantics/metamodel/typer/checker.py +6 -4
- v0/relationalai/semantics/metamodel/typer/typer.py +4 -3
- v0/relationalai/semantics/metamodel/visitor.py +4 -3
- v0/relationalai/semantics/reasoners/optimization/solvers_dev.py +1 -1
- v0/relationalai/semantics/reasoners/optimization/solvers_pb.py +336 -86
- v0/relationalai/semantics/rel/compiler.py +2 -1
- v0/relationalai/semantics/rel/executor.py +3 -2
- v0/relationalai/semantics/tests/lqp/__init__.py +0 -0
- v0/relationalai/semantics/tests/lqp/algorithms.py +345 -0
- v0/relationalai/tools/cli.py +339 -186
- v0/relationalai/tools/cli_controls.py +216 -67
- v0/relationalai/tools/cli_helpers.py +410 -6
- v0/relationalai/util/format.py +5 -2
- {relationalai-1.0.0a3.dist-info → relationalai-1.0.0a5.dist-info}/entry_points.txt +0 -0
- {relationalai-1.0.0a3.dist-info → relationalai-1.0.0a5.dist-info}/top_level.txt +0 -0
|
@@ -1,11 +1,21 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Number manipulation functions.
|
|
3
|
+
|
|
4
|
+
This module provides functions for working with numbers including:
|
|
5
|
+
- Number construction with precision and scale
|
|
6
|
+
- Integer operations
|
|
7
|
+
- Parsing numbers from strings
|
|
8
|
+
- Querying number properties (precision, scale, size)
|
|
9
|
+
"""
|
|
1
10
|
from __future__ import annotations
|
|
2
11
|
|
|
3
12
|
from . import NumberValue, StringValue
|
|
4
|
-
from .. import literal
|
|
5
13
|
from ..frontend.base import Library, Concept, MetaRef, NumberConcept, Expression, Field, Variable
|
|
6
|
-
from ..frontend.core import Number, String, cast as core_cast
|
|
14
|
+
from ..frontend.core import Number, String, Integer, cast as core_cast
|
|
7
15
|
from decimal import Decimal as PyDecimal
|
|
16
|
+
from relationalai.util.docutils import include_in_docs
|
|
8
17
|
|
|
18
|
+
__include_in_docs__ = True
|
|
9
19
|
|
|
10
20
|
# the front-end library object
|
|
11
21
|
library = Library("numbers")
|
|
@@ -15,32 +25,119 @@ library = Library("numbers")
|
|
|
15
25
|
#--------------------------------------------------
|
|
16
26
|
_parse_number = library.Relation("parse_number", [Field.input("value", String), Field("result", Number)])
|
|
17
27
|
|
|
18
|
-
|
|
28
|
+
@include_in_docs
|
|
29
|
+
def number(value: NumberValue, precision:int=38, scale:int=14) -> Variable:
|
|
19
30
|
"""
|
|
20
|
-
Create an expression that represents a number with
|
|
31
|
+
Create an expression that represents a number with specified value, precision and scale.
|
|
32
|
+
|
|
33
|
+
Parameters
|
|
34
|
+
----------
|
|
35
|
+
value: NumberValue
|
|
36
|
+
The numeric value.
|
|
37
|
+
precision: int
|
|
38
|
+
The precision (total number of digits).
|
|
39
|
+
scale: int
|
|
40
|
+
The scale (number of decimal places).
|
|
41
|
+
|
|
42
|
+
Returns
|
|
43
|
+
-------
|
|
44
|
+
Variable
|
|
45
|
+
A `Variable` representing the number with the specified properties. Returns `Number`.
|
|
46
|
+
|
|
47
|
+
Examples
|
|
48
|
+
--------
|
|
49
|
+
Create a number with custom precision and scale:
|
|
50
|
+
|
|
51
|
+
>>> numbers.number(123.456, precision=10, scale=3)
|
|
52
|
+
>>> numbers.number(Product.price, precision=12, scale=2)
|
|
21
53
|
"""
|
|
22
54
|
if isinstance(value, Variable):
|
|
23
55
|
cast_type = Number.size(precision, scale)
|
|
24
56
|
return core_cast(MetaRef(cast_type), value, cast_type.ref())
|
|
25
57
|
|
|
26
58
|
# literals
|
|
27
|
-
if isinstance(value, int):
|
|
59
|
+
if isinstance(value, int) and scale != 0:
|
|
28
60
|
value = PyDecimal(str(value))
|
|
29
61
|
if isinstance(value, float):
|
|
30
62
|
value = PyDecimal(str(value))
|
|
31
|
-
return
|
|
63
|
+
return Number.size(precision, scale)(value)
|
|
32
64
|
|
|
33
|
-
|
|
65
|
+
@include_in_docs
|
|
66
|
+
def integer(value: NumberValue) -> Variable:
|
|
34
67
|
"""
|
|
35
|
-
Create an expression that represents
|
|
36
|
-
|
|
68
|
+
Create an expression that represents an integer with this value.
|
|
69
|
+
|
|
70
|
+
Parameters
|
|
71
|
+
----------
|
|
72
|
+
value: NumberValue
|
|
73
|
+
The integer value.
|
|
74
|
+
|
|
75
|
+
Returns
|
|
76
|
+
-------
|
|
77
|
+
Variable
|
|
78
|
+
A `Variable` representing the integer value. Returns `Integer`.
|
|
79
|
+
|
|
80
|
+
Examples
|
|
81
|
+
--------
|
|
82
|
+
Create integer expressions:
|
|
83
|
+
|
|
84
|
+
>>> numbers.integer(42)
|
|
85
|
+
>>> numbers.integer(Employee.age)
|
|
86
|
+
"""
|
|
87
|
+
if isinstance(value, Variable):
|
|
88
|
+
return core_cast(MetaRef(Integer), value, Integer.ref())
|
|
89
|
+
return Integer(value)
|
|
90
|
+
|
|
91
|
+
@include_in_docs
|
|
92
|
+
def parse_number(value: StringValue, precision:int=38, scale:int=14) -> Expression:
|
|
93
|
+
"""
|
|
94
|
+
Parse a string value as a number with specified precision and scale.
|
|
95
|
+
|
|
96
|
+
Parameters
|
|
97
|
+
----------
|
|
98
|
+
value: StringValue
|
|
99
|
+
The string value to parse.
|
|
100
|
+
precision: int
|
|
101
|
+
The precision (total number of digits).
|
|
102
|
+
scale: int
|
|
103
|
+
The scale (number of decimal places).
|
|
104
|
+
|
|
105
|
+
Returns
|
|
106
|
+
-------
|
|
107
|
+
Expression
|
|
108
|
+
An `Expression` computing the parsed number. Returns `Number`.
|
|
109
|
+
|
|
110
|
+
Examples
|
|
111
|
+
--------
|
|
112
|
+
Parse string to number:
|
|
113
|
+
|
|
114
|
+
>>> numbers.parse_number("123.456")
|
|
115
|
+
>>> numbers.parse_number(Product.price_str, precision=10, scale=2)
|
|
37
116
|
"""
|
|
38
117
|
return _parse_number(value, Number.size(precision, scale).ref())
|
|
39
118
|
|
|
119
|
+
@include_in_docs
|
|
40
120
|
def parse(value: str, number: NumberConcept) -> Expression:
|
|
41
121
|
"""
|
|
42
|
-
|
|
43
|
-
|
|
122
|
+
Parse a string value as a number with the precision and scale of the number argument.
|
|
123
|
+
|
|
124
|
+
Parameters
|
|
125
|
+
----------
|
|
126
|
+
value: str
|
|
127
|
+
The string value to parse.
|
|
128
|
+
number: NumberConcept
|
|
129
|
+
The NumberConcept specifying the target precision and scale.
|
|
130
|
+
|
|
131
|
+
Returns
|
|
132
|
+
-------
|
|
133
|
+
Expression
|
|
134
|
+
An `Expression` computing the parsed number. Returns `Number`.
|
|
135
|
+
|
|
136
|
+
Examples
|
|
137
|
+
--------
|
|
138
|
+
Parse string using another number's format:
|
|
139
|
+
|
|
140
|
+
>>> numbers.parse("999.99", Product.price)
|
|
44
141
|
"""
|
|
45
142
|
return parse_number(value, precision(number), scale(number))
|
|
46
143
|
|
|
@@ -48,30 +145,129 @@ def parse(value: str, number: NumberConcept) -> Expression:
|
|
|
48
145
|
# Number information.
|
|
49
146
|
#--------------------------------------------------
|
|
50
147
|
|
|
148
|
+
@include_in_docs
|
|
51
149
|
def is_number(number: Concept) -> bool:
|
|
150
|
+
"""
|
|
151
|
+
Check if a concept represents a number.
|
|
152
|
+
|
|
153
|
+
Parameters
|
|
154
|
+
----------
|
|
155
|
+
number: Concept
|
|
156
|
+
The Concept to check.
|
|
157
|
+
|
|
158
|
+
Returns
|
|
159
|
+
-------
|
|
160
|
+
bool
|
|
161
|
+
True if the concept is a NumberConcept.
|
|
162
|
+
|
|
163
|
+
Examples
|
|
164
|
+
--------
|
|
165
|
+
Check if a concept is a number:
|
|
166
|
+
|
|
167
|
+
>>> numbers.is_number(Product.price)
|
|
168
|
+
"""
|
|
52
169
|
return isinstance(number, NumberConcept)
|
|
53
170
|
|
|
171
|
+
@include_in_docs
|
|
54
172
|
def precision(number: NumberConcept) -> int:
|
|
55
|
-
"""
|
|
173
|
+
"""
|
|
174
|
+
Get the precision (total number of digits) of a number concept.
|
|
175
|
+
|
|
176
|
+
Parameters
|
|
177
|
+
----------
|
|
178
|
+
number: NumberConcept
|
|
179
|
+
The NumberConcept to query.
|
|
180
|
+
|
|
181
|
+
Returns
|
|
182
|
+
-------
|
|
183
|
+
int
|
|
184
|
+
The precision of the number.
|
|
185
|
+
|
|
186
|
+
Examples
|
|
187
|
+
--------
|
|
188
|
+
Get the precision of a number concept:
|
|
189
|
+
|
|
190
|
+
>>> numbers.precision(Product.price)
|
|
191
|
+
"""
|
|
56
192
|
return number._precision
|
|
57
193
|
|
|
194
|
+
@include_in_docs
|
|
58
195
|
def scale(number: NumberConcept) -> int:
|
|
59
|
-
"""
|
|
196
|
+
"""
|
|
197
|
+
Get the scale (number of decimal places) of a number concept.
|
|
198
|
+
|
|
199
|
+
Parameters
|
|
200
|
+
----------
|
|
201
|
+
number: NumberConcept
|
|
202
|
+
The NumberConcept to query.
|
|
203
|
+
|
|
204
|
+
Returns
|
|
205
|
+
-------
|
|
206
|
+
int
|
|
207
|
+
The scale of the number.
|
|
208
|
+
|
|
209
|
+
Examples
|
|
210
|
+
--------
|
|
211
|
+
Get the scale of a number concept:
|
|
212
|
+
|
|
213
|
+
>>> numbers.scale(Product.price)
|
|
214
|
+
"""
|
|
60
215
|
return number._scale
|
|
61
216
|
|
|
217
|
+
@include_in_docs
|
|
62
218
|
def size(number: NumberConcept) -> int:
|
|
63
219
|
"""
|
|
64
|
-
|
|
65
|
-
|
|
220
|
+
Get the size (number of bits) needed to represent a number concept.
|
|
221
|
+
|
|
222
|
+
Parameters
|
|
223
|
+
----------
|
|
224
|
+
number: NumberConcept
|
|
225
|
+
The NumberConcept to query.
|
|
226
|
+
|
|
227
|
+
Returns
|
|
228
|
+
-------
|
|
229
|
+
int
|
|
230
|
+
The size in bits needed to represent the number.
|
|
231
|
+
|
|
232
|
+
Examples
|
|
233
|
+
--------
|
|
234
|
+
Get the bit size of a number concept:
|
|
235
|
+
|
|
236
|
+
>>> numbers.size(Product.price)
|
|
66
237
|
"""
|
|
67
|
-
return digits_to_bits(number.
|
|
238
|
+
return digits_to_bits(number._precision)
|
|
68
239
|
|
|
69
|
-
|
|
240
|
+
@include_in_docs
|
|
241
|
+
def digits_to_bits(precision: int) -> int:
|
|
70
242
|
"""
|
|
71
|
-
Transform from a number of base 10 digits to the number of bits necessary to represent
|
|
72
|
-
|
|
243
|
+
Transform from a number of base 10 digits to the number of bits necessary to represent it.
|
|
244
|
+
|
|
245
|
+
Parameters
|
|
246
|
+
----------
|
|
247
|
+
precision: int
|
|
248
|
+
The number of base 10 digits.
|
|
249
|
+
|
|
250
|
+
Returns
|
|
251
|
+
-------
|
|
252
|
+
int
|
|
253
|
+
The number of bits required (8, 16, 32, 64, or 128).
|
|
254
|
+
|
|
255
|
+
Raises
|
|
256
|
+
------
|
|
257
|
+
ValueError
|
|
258
|
+
If precision is greater than 38 or invalid.
|
|
259
|
+
|
|
260
|
+
Examples
|
|
261
|
+
--------
|
|
262
|
+
A number with 38 digits requires 128 bits:
|
|
263
|
+
|
|
264
|
+
>>> numbers.digits_to_bits(38)
|
|
265
|
+
128
|
|
266
|
+
|
|
267
|
+
A number with 9 digits requires 32 bits:
|
|
73
268
|
|
|
74
|
-
|
|
269
|
+
>>> numbers.digits_to_bits(9)
|
|
270
|
+
32
|
|
75
271
|
"""
|
|
76
272
|
if precision <= 2:
|
|
77
273
|
return 8
|
relationalai/semantics/std/re.py
CHANGED
|
@@ -1,11 +1,23 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Regular expression function.
|
|
3
|
+
|
|
4
|
+
This module provides regex pattern matching and manipulation functions including:
|
|
5
|
+
- Pattern matching
|
|
6
|
+
- Finding all matches
|
|
7
|
+
- String substitution
|
|
8
|
+
- Match object with position and grouping support
|
|
9
|
+
"""
|
|
1
10
|
from __future__ import annotations
|
|
2
11
|
|
|
3
12
|
|
|
4
13
|
from . import StringValue, IntegerValue, strings, _function_not_implemented
|
|
5
14
|
from ..frontend.base import Library, Expression, Field, Variable
|
|
6
|
-
from ..frontend.core import
|
|
15
|
+
from ..frontend.core import String, Integer
|
|
16
|
+
from relationalai.util.docutils import include_in_docs
|
|
7
17
|
from enum import Enum
|
|
8
18
|
|
|
19
|
+
__include_in_docs__ = True
|
|
20
|
+
|
|
9
21
|
# the front-end library object
|
|
10
22
|
library = Library("re")
|
|
11
23
|
|
|
@@ -27,21 +39,106 @@ _regex_match_all = library.Relation("regex_match_all", [Field.input("regex", Str
|
|
|
27
39
|
|
|
28
40
|
# match is ^{REGEX} , search is {REGEX}, and fullmatch is ^{REGEX}$
|
|
29
41
|
|
|
42
|
+
@include_in_docs
|
|
30
43
|
def match(regex: StringValue, value: StringValue) -> RegexMatch:
|
|
31
|
-
"""
|
|
44
|
+
"""
|
|
45
|
+
Check if the regex matches the value from the start.
|
|
46
|
+
|
|
47
|
+
Parameters
|
|
48
|
+
----------
|
|
49
|
+
regex: StringValue
|
|
50
|
+
The regular expression pattern.
|
|
51
|
+
value: StringValue
|
|
52
|
+
The string value to match against.
|
|
53
|
+
|
|
54
|
+
Returns
|
|
55
|
+
-------
|
|
56
|
+
RegexMatch
|
|
57
|
+
A `RegexMatch` object representing the match result.
|
|
58
|
+
|
|
59
|
+
Examples
|
|
60
|
+
--------
|
|
61
|
+
Match a pattern from the start of a string:
|
|
62
|
+
|
|
63
|
+
>>> re.match(r"S.*", Person.name)
|
|
64
|
+
>>> select(Person.name).where(re.match(r"^[A-Z]", Person.name))
|
|
65
|
+
"""
|
|
32
66
|
# REGEXP_LIKE
|
|
33
67
|
return RegexMatch(regex, value, type=RegexMatchType.MATCH)
|
|
34
68
|
|
|
69
|
+
@include_in_docs
|
|
35
70
|
def search(regex: StringValue, value: StringValue, pos: IntegerValue = 0) -> RegexMatch:
|
|
71
|
+
"""
|
|
72
|
+
Search for the regex pattern in the value starting at the given position.
|
|
73
|
+
|
|
74
|
+
.. note::
|
|
75
|
+
This function is not yet implemented.
|
|
76
|
+
|
|
77
|
+
Parameters
|
|
78
|
+
----------
|
|
79
|
+
regex: StringValue
|
|
80
|
+
The regular expression pattern.
|
|
81
|
+
value: StringValue
|
|
82
|
+
The string value to search in.
|
|
83
|
+
pos: IntegerValue
|
|
84
|
+
The starting position for the search (0-based). Default: 0.
|
|
85
|
+
|
|
86
|
+
Returns
|
|
87
|
+
-------
|
|
88
|
+
RegexMatch
|
|
89
|
+
A `RegexMatch` object representing the search result.
|
|
90
|
+
"""
|
|
36
91
|
_function_not_implemented("re.search")
|
|
37
92
|
return RegexMatch(regex, value, pos, type=RegexMatchType.SEARCH)
|
|
38
93
|
|
|
94
|
+
@include_in_docs
|
|
39
95
|
def fullmatch(regex: StringValue, value: StringValue, pos: IntegerValue = 0) -> RegexMatch:
|
|
40
|
-
"""
|
|
96
|
+
"""
|
|
97
|
+
Check if the regex matches the entire value starting at the given position.
|
|
98
|
+
|
|
99
|
+
Parameters
|
|
100
|
+
----------
|
|
101
|
+
regex: StringValue
|
|
102
|
+
The regular expression pattern.
|
|
103
|
+
value: StringValue
|
|
104
|
+
The string value to match against.
|
|
105
|
+
pos: IntegerValue
|
|
106
|
+
The starting position for the match (0-based). Default: 0.
|
|
107
|
+
|
|
108
|
+
Returns
|
|
109
|
+
-------
|
|
110
|
+
RegexMatch
|
|
111
|
+
A `RegexMatch` object representing the match result.
|
|
112
|
+
|
|
113
|
+
Examples
|
|
114
|
+
--------
|
|
115
|
+
Match entire string against a pattern:
|
|
116
|
+
|
|
117
|
+
>>> re.fullmatch(r"[A-Z][a-z]+", Person.name)
|
|
118
|
+
>>> select(Email.address).where(re.fullmatch(r"\\w+@\\w+\\.\\w+", Email.address))
|
|
119
|
+
"""
|
|
41
120
|
return RegexMatch(regex, value, pos, type=RegexMatchType.FULLMATCH)
|
|
42
121
|
|
|
122
|
+
@include_in_docs
|
|
43
123
|
def findall(regex: StringValue, value: StringValue) -> tuple[Variable, Variable]:
|
|
44
|
-
"""
|
|
124
|
+
"""
|
|
125
|
+
Find all non-overlapping matches of the regex in the value.
|
|
126
|
+
|
|
127
|
+
.. note::
|
|
128
|
+
This function is not yet implemented.
|
|
129
|
+
|
|
130
|
+
Parameters
|
|
131
|
+
----------
|
|
132
|
+
regex: StringValue
|
|
133
|
+
The regular expression pattern.
|
|
134
|
+
value: StringValue
|
|
135
|
+
The string value to search in.
|
|
136
|
+
|
|
137
|
+
Returns
|
|
138
|
+
-------
|
|
139
|
+
tuple[Variable, Variable]
|
|
140
|
+
A tuple of (position, match) `Variable`s representing all matches. Position is `Integer`, match is `String`.
|
|
141
|
+
"""
|
|
45
142
|
_function_not_implemented("re.findall")
|
|
46
143
|
# exp = _regex_match_all(regex, value)
|
|
47
144
|
# ix, match = exp._arg_ref(2), exp._arg_ref(3)
|
|
@@ -49,19 +146,69 @@ def findall(regex: StringValue, value: StringValue) -> tuple[Variable, Variable]
|
|
|
49
146
|
# return rank, match
|
|
50
147
|
raise
|
|
51
148
|
|
|
149
|
+
@include_in_docs
|
|
52
150
|
def sub(regex: StringValue, repl: StringValue, value: StringValue):
|
|
53
|
-
"""
|
|
151
|
+
"""
|
|
152
|
+
Replace occurrences of the regex in the value with the replacement string.
|
|
153
|
+
|
|
154
|
+
.. note::
|
|
155
|
+
This function is not yet implemented.
|
|
156
|
+
|
|
157
|
+
Parameters
|
|
158
|
+
----------
|
|
159
|
+
regex: StringValue
|
|
160
|
+
The regular expression pattern.
|
|
161
|
+
repl: StringValue
|
|
162
|
+
The replacement string.
|
|
163
|
+
value: StringValue
|
|
164
|
+
The string value to perform substitution on.
|
|
165
|
+
|
|
166
|
+
Returns
|
|
167
|
+
-------
|
|
168
|
+
Expression
|
|
169
|
+
An `Expression` computing the substituted string. Returns `String`.
|
|
170
|
+
"""
|
|
54
171
|
_function_not_implemented("re.sub")
|
|
55
172
|
# return _regex_replace(regex, repl, value)
|
|
56
173
|
|
|
57
174
|
class RegexMatchType(Enum):
|
|
175
|
+
"""Enumeration of regex match types."""
|
|
58
176
|
MATCH = "match"
|
|
59
177
|
SEARCH = "search"
|
|
60
178
|
FULLMATCH = "fullmatch"
|
|
61
179
|
|
|
180
|
+
@include_in_docs
|
|
62
181
|
class RegexMatch(Expression):
|
|
182
|
+
"""
|
|
183
|
+
Represents a regex match result with position and grouping support.
|
|
184
|
+
|
|
185
|
+
Attributes
|
|
186
|
+
----------
|
|
187
|
+
regex: StringValue
|
|
188
|
+
The regex pattern used for matching.
|
|
189
|
+
value: StringValue
|
|
190
|
+
The string value being matched.
|
|
191
|
+
pos: IntegerValue
|
|
192
|
+
The starting position of the match (0-based).
|
|
193
|
+
match: Variable
|
|
194
|
+
A Variable representing the matched string.
|
|
195
|
+
"""
|
|
63
196
|
|
|
64
197
|
def __init__(self, regex: StringValue, value: StringValue, pos: IntegerValue = 0, type=RegexMatchType.MATCH):
|
|
198
|
+
"""
|
|
199
|
+
Initialize a RegexMatch object.
|
|
200
|
+
|
|
201
|
+
Parameters
|
|
202
|
+
----------
|
|
203
|
+
regex: StringValue
|
|
204
|
+
The regular expression pattern.
|
|
205
|
+
value: StringValue
|
|
206
|
+
The string value to match against.
|
|
207
|
+
pos: IntegerValue
|
|
208
|
+
The starting position for the match (0-based). Default: 0.
|
|
209
|
+
type: RegexMatchType
|
|
210
|
+
The type of match (MATCH, SEARCH, or FULLMATCH). Default: MATCH.
|
|
211
|
+
"""
|
|
65
212
|
if type == RegexMatchType.FULLMATCH:
|
|
66
213
|
# fullmatch: ^{REGEX}$
|
|
67
214
|
self.regex = strings.concat(regex, "$")
|
|
@@ -74,20 +221,81 @@ class RegexMatch(Expression):
|
|
|
74
221
|
self.match = String.ref("match")
|
|
75
222
|
super().__init__(_regex_match_all, (self.regex, value, self.pos, self.match))
|
|
76
223
|
|
|
224
|
+
@include_in_docs
|
|
77
225
|
def start(self) -> IntegerValue:
|
|
226
|
+
"""
|
|
227
|
+
Get the starting position of the match.
|
|
228
|
+
|
|
229
|
+
Returns
|
|
230
|
+
-------
|
|
231
|
+
IntegerValue
|
|
232
|
+
The 0-based starting position of the match.
|
|
233
|
+
"""
|
|
78
234
|
return self.pos
|
|
79
235
|
|
|
236
|
+
@include_in_docs
|
|
80
237
|
def end(self) -> IntegerValue:
|
|
238
|
+
"""
|
|
239
|
+
Get the ending position of the match.
|
|
240
|
+
|
|
241
|
+
Returns
|
|
242
|
+
-------
|
|
243
|
+
IntegerValue
|
|
244
|
+
The 0-based ending position of the match.
|
|
245
|
+
"""
|
|
81
246
|
return strings.len(self.match) + self.pos - 1
|
|
82
247
|
|
|
248
|
+
@include_in_docs
|
|
83
249
|
def span(self) -> tuple[IntegerValue, IntegerValue]:
|
|
250
|
+
"""
|
|
251
|
+
Get the start and end positions of the match.
|
|
252
|
+
|
|
253
|
+
Returns
|
|
254
|
+
-------
|
|
255
|
+
tuple[IntegerValue, IntegerValue]
|
|
256
|
+
A tuple of (start, end) positions.
|
|
257
|
+
"""
|
|
84
258
|
return self.start(), self.end()
|
|
85
259
|
|
|
260
|
+
@include_in_docs
|
|
86
261
|
def group(self, index: IntegerValue = 0) -> Variable:
|
|
262
|
+
"""
|
|
263
|
+
Get a capture group by index.
|
|
264
|
+
|
|
265
|
+
.. note::
|
|
266
|
+
This method is not yet implemented.
|
|
267
|
+
|
|
268
|
+
Parameters
|
|
269
|
+
----------
|
|
270
|
+
index: IntegerValue
|
|
271
|
+
The index of the capture group (0 for the entire match). Default: 0.
|
|
272
|
+
|
|
273
|
+
Returns
|
|
274
|
+
-------
|
|
275
|
+
Variable
|
|
276
|
+
A Variable representing the capture group content.
|
|
277
|
+
"""
|
|
87
278
|
_function_not_implemented("re.RegexMatch.group")
|
|
88
279
|
raise
|
|
89
280
|
|
|
281
|
+
@include_in_docs
|
|
90
282
|
def group_by_name(self, name: StringValue) -> Variable:
|
|
283
|
+
"""
|
|
284
|
+
Get a named capture group.
|
|
285
|
+
|
|
286
|
+
.. note::
|
|
287
|
+
This method is not yet implemented.
|
|
288
|
+
|
|
289
|
+
Parameters
|
|
290
|
+
----------
|
|
291
|
+
name: StringValue
|
|
292
|
+
The name of the capture group.
|
|
293
|
+
|
|
294
|
+
Returns
|
|
295
|
+
-------
|
|
296
|
+
Variable
|
|
297
|
+
A Variable representing the named capture group content.
|
|
298
|
+
"""
|
|
91
299
|
_function_not_implemented("re.RegexMatch.group_by_name")
|
|
92
300
|
raise
|
|
93
301
|
|