pulse-framework 0.1.51__py3-none-any.whl → 0.1.53__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.
- pulse/__init__.py +542 -562
- pulse/_examples.py +29 -0
- pulse/app.py +0 -14
- pulse/cli/cmd.py +96 -80
- pulse/cli/dependencies.py +10 -41
- pulse/cli/folder_lock.py +3 -3
- pulse/cli/helpers.py +40 -67
- pulse/cli/logging.py +102 -0
- pulse/cli/packages.py +16 -0
- pulse/cli/processes.py +40 -23
- pulse/codegen/codegen.py +70 -35
- pulse/codegen/js.py +2 -4
- pulse/codegen/templates/route.py +94 -146
- pulse/component.py +115 -0
- pulse/components/for_.py +1 -1
- pulse/components/if_.py +1 -1
- pulse/components/react_router.py +16 -22
- pulse/{html → dom}/events.py +1 -1
- pulse/{html → dom}/props.py +6 -6
- pulse/{html → dom}/tags.py +11 -11
- pulse/dom/tags.pyi +480 -0
- pulse/form.py +7 -6
- pulse/hooks/init.py +1 -13
- pulse/js/__init__.py +37 -41
- pulse/js/__init__.pyi +22 -2
- pulse/js/_types.py +5 -3
- pulse/js/array.py +121 -38
- pulse/js/console.py +9 -9
- pulse/js/date.py +22 -19
- pulse/js/document.py +8 -4
- pulse/js/error.py +12 -14
- pulse/js/json.py +4 -3
- pulse/js/map.py +17 -7
- pulse/js/math.py +2 -2
- pulse/js/navigator.py +4 -4
- pulse/js/number.py +8 -8
- pulse/js/object.py +9 -13
- pulse/js/promise.py +25 -9
- pulse/js/regexp.py +6 -6
- pulse/js/set.py +20 -8
- pulse/js/string.py +7 -7
- pulse/js/weakmap.py +6 -6
- pulse/js/weakset.py +6 -6
- pulse/js/window.py +17 -14
- pulse/messages.py +1 -4
- pulse/react_component.py +3 -1001
- pulse/render_session.py +74 -66
- pulse/renderer.py +311 -238
- pulse/routing.py +1 -10
- pulse/transpiler/__init__.py +84 -114
- pulse/transpiler/builtins.py +661 -343
- pulse/transpiler/errors.py +78 -2
- pulse/transpiler/function.py +463 -133
- pulse/transpiler/id.py +18 -0
- pulse/transpiler/imports.py +230 -325
- pulse/transpiler/js_module.py +218 -209
- pulse/transpiler/modules/__init__.py +16 -13
- pulse/transpiler/modules/asyncio.py +45 -26
- pulse/transpiler/modules/json.py +12 -8
- pulse/transpiler/modules/math.py +161 -216
- pulse/transpiler/modules/pulse/__init__.py +5 -0
- pulse/transpiler/modules/pulse/tags.py +231 -0
- pulse/transpiler/modules/typing.py +33 -28
- pulse/transpiler/nodes.py +1607 -923
- pulse/transpiler/py_module.py +118 -95
- pulse/transpiler/react_component.py +51 -0
- pulse/transpiler/transpiler.py +593 -437
- pulse/transpiler/vdom.py +255 -0
- {pulse_framework-0.1.51.dist-info → pulse_framework-0.1.53.dist-info}/METADATA +1 -1
- pulse_framework-0.1.53.dist-info/RECORD +120 -0
- pulse/html/tags.pyi +0 -470
- pulse/transpiler/constants.py +0 -110
- pulse/transpiler/context.py +0 -26
- pulse/transpiler/ids.py +0 -16
- pulse/transpiler/modules/re.py +0 -466
- pulse/transpiler/modules/tags.py +0 -268
- pulse/transpiler/utils.py +0 -4
- pulse/vdom.py +0 -599
- pulse_framework-0.1.51.dist-info/RECORD +0 -119
- /pulse/{html → dom}/__init__.py +0 -0
- /pulse/{html → dom}/elements.py +0 -0
- /pulse/{html → dom}/svg.py +0 -0
- {pulse_framework-0.1.51.dist-info → pulse_framework-0.1.53.dist-info}/WHEEL +0 -0
- {pulse_framework-0.1.51.dist-info → pulse_framework-0.1.53.dist-info}/entry_points.txt +0 -0
pulse/transpiler/modules/re.py
DELETED
|
@@ -1,466 +0,0 @@
|
|
|
1
|
-
"""Python re module transpilation to JavaScript RegExp.
|
|
2
|
-
|
|
3
|
-
This module provides transpilation from Python's `re` module to JavaScript's RegExp.
|
|
4
|
-
For direct JavaScript RegExp bindings, use `pulse.js.regexp` instead.
|
|
5
|
-
|
|
6
|
-
Supported features:
|
|
7
|
-
- re.match, re.search, re.fullmatch, re.sub, re.split, re.findall, re.compile
|
|
8
|
-
- Flags: re.I, re.M, re.S, re.U (re.VERBOSE/re.X is NOT supported)
|
|
9
|
-
- Named groups: (?P<name>...) → (?<name>...)
|
|
10
|
-
- Named backrefs: (?P=name) → \\k<name>
|
|
11
|
-
- Replacement backrefs: \\g<name> → $<name>, \\1 → $1
|
|
12
|
-
|
|
13
|
-
Limitations:
|
|
14
|
-
- re.VERBOSE (re.X) is not supported - no JS equivalent
|
|
15
|
-
- Conditional patterns (?(id)yes|no) are not supported
|
|
16
|
-
- \\A and \\Z have different semantics with multiline mode
|
|
17
|
-
"""
|
|
18
|
-
|
|
19
|
-
# pyright: reportUnannotatedClassAttribute=false
|
|
20
|
-
|
|
21
|
-
import re as re_module
|
|
22
|
-
|
|
23
|
-
from pulse.transpiler.constants import jsify
|
|
24
|
-
from pulse.transpiler.errors import JSCompilationError
|
|
25
|
-
from pulse.transpiler.nodes import (
|
|
26
|
-
JSArray,
|
|
27
|
-
JSArrowFunction,
|
|
28
|
-
JSExpr,
|
|
29
|
-
JSIdentifier,
|
|
30
|
-
JSMemberCall,
|
|
31
|
-
JSNew,
|
|
32
|
-
JSNumber,
|
|
33
|
-
JSSpread,
|
|
34
|
-
JSString,
|
|
35
|
-
JSSubscript,
|
|
36
|
-
)
|
|
37
|
-
from pulse.transpiler.py_module import PyModule
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def _convert_pattern(pattern: str) -> str:
|
|
41
|
-
"""Convert Python regex syntax to JavaScript regex syntax.
|
|
42
|
-
|
|
43
|
-
Handles:
|
|
44
|
-
- (?P<name>...) → (?<name>...)
|
|
45
|
-
- (?P=name) → \\k<name>
|
|
46
|
-
"""
|
|
47
|
-
import re
|
|
48
|
-
|
|
49
|
-
# Convert named groups: (?P<name>...) → (?<name>...)
|
|
50
|
-
result = re.sub(r"\(\?P<([^>]+)>", r"(?<\1>", pattern)
|
|
51
|
-
|
|
52
|
-
# Convert named backreferences: (?P=name) → \k<name>
|
|
53
|
-
result = re.sub(r"\(\?P=([^)]+)\)", r"\\k<\1>", result)
|
|
54
|
-
|
|
55
|
-
return result
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
def _convert_replacement(replacement: str) -> str:
|
|
59
|
-
"""Convert Python replacement string syntax to JavaScript.
|
|
60
|
-
|
|
61
|
-
Handles:
|
|
62
|
-
- \\g<name> → $<name>
|
|
63
|
-
- \\g<0>, \\g<1>, etc. → $0, $1, etc.
|
|
64
|
-
- \\1, \\2, etc. → $1, $2, etc. (already handled by JS)
|
|
65
|
-
"""
|
|
66
|
-
import re
|
|
67
|
-
|
|
68
|
-
# Convert named group references: \g<name> → $<name>
|
|
69
|
-
result = re.sub(r"\\g<([^>]+)>", r"$<\1>", replacement)
|
|
70
|
-
|
|
71
|
-
# Convert numeric group references: \1 → $1 (for replacement strings)
|
|
72
|
-
# Note: In Python replacement, \1 means group 1. In JS, it's $1.
|
|
73
|
-
result = re.sub(r"\\([1-9][0-9]*)", r"$\1", result)
|
|
74
|
-
|
|
75
|
-
return result
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
def _get_pattern_string(pattern: JSExpr) -> str | None:
|
|
79
|
-
"""Extract the string value from a JSString pattern, or None if dynamic."""
|
|
80
|
-
if isinstance(pattern, JSString):
|
|
81
|
-
return pattern.value
|
|
82
|
-
return None
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
def _make_regexp(
|
|
86
|
-
pattern: JSExpr,
|
|
87
|
-
flags: str = "",
|
|
88
|
-
*,
|
|
89
|
-
anchor_start: bool = False,
|
|
90
|
-
anchor_both: bool = False,
|
|
91
|
-
) -> JSExpr:
|
|
92
|
-
"""Create a new RegExp expression, optionally converting the pattern."""
|
|
93
|
-
pattern_str = _get_pattern_string(pattern)
|
|
94
|
-
|
|
95
|
-
if pattern_str is not None:
|
|
96
|
-
# Static pattern - convert at transpile time
|
|
97
|
-
converted = _convert_pattern(pattern_str)
|
|
98
|
-
if anchor_start and not converted.startswith("^"):
|
|
99
|
-
converted = "^" + converted
|
|
100
|
-
if anchor_both:
|
|
101
|
-
if not converted.startswith("^"):
|
|
102
|
-
converted = "^" + converted
|
|
103
|
-
if not converted.endswith("$"):
|
|
104
|
-
converted = converted + "$"
|
|
105
|
-
pattern = JSString(converted)
|
|
106
|
-
|
|
107
|
-
args: list[JSExpr] = [pattern]
|
|
108
|
-
if flags:
|
|
109
|
-
args.append(JSString(flags))
|
|
110
|
-
|
|
111
|
-
return JSNew(JSIdentifier("RegExp"), args)
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
# Flag constants - these emit their JS equivalent flag character
|
|
115
|
-
class _FlagExpr(JSExpr):
|
|
116
|
-
"""A regex flag that emits as a string character."""
|
|
117
|
-
|
|
118
|
-
def __init__(self, flag: str, name: str):
|
|
119
|
-
self.flag = flag
|
|
120
|
-
self.name = name
|
|
121
|
-
|
|
122
|
-
def emit(self) -> str: # pyright: ignore[reportImplicitOverride]
|
|
123
|
-
return f'"{self.flag}"'
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
class PyRe(PyModule):
|
|
127
|
-
"""Provides transpilation for Python re functions to JavaScript RegExp."""
|
|
128
|
-
|
|
129
|
-
# Flag constants
|
|
130
|
-
I = _FlagExpr("i", "IGNORECASE") # noqa: E741
|
|
131
|
-
IGNORECASE = _FlagExpr("i", "IGNORECASE")
|
|
132
|
-
M = _FlagExpr("m", "MULTILINE")
|
|
133
|
-
MULTILINE = _FlagExpr("m", "MULTILINE")
|
|
134
|
-
S = _FlagExpr("s", "DOTALL")
|
|
135
|
-
DOTALL = _FlagExpr("s", "DOTALL")
|
|
136
|
-
U = _FlagExpr("u", "UNICODE")
|
|
137
|
-
UNICODE = _FlagExpr("u", "UNICODE")
|
|
138
|
-
|
|
139
|
-
# Unsupported flags - will error at transpile time
|
|
140
|
-
@staticmethod
|
|
141
|
-
def _unsupported_flag(name: str) -> JSExpr:
|
|
142
|
-
raise JSCompilationError(
|
|
143
|
-
f"re.{name} (VERBOSE) flag is not supported - no JavaScript equivalent"
|
|
144
|
-
)
|
|
145
|
-
|
|
146
|
-
X = property(lambda self: PyRe._unsupported_flag("X"))
|
|
147
|
-
VERBOSE = property(lambda self: PyRe._unsupported_flag("VERBOSE"))
|
|
148
|
-
A = property(lambda self: PyRe._unsupported_flag("A"))
|
|
149
|
-
ASCII = property(lambda self: PyRe._unsupported_flag("ASCII"))
|
|
150
|
-
|
|
151
|
-
@staticmethod
|
|
152
|
-
def compile(
|
|
153
|
-
pattern: str | JSExpr,
|
|
154
|
-
flags: int | JSExpr = 0,
|
|
155
|
-
) -> JSExpr:
|
|
156
|
-
"""Compile a pattern into a RegExp object.
|
|
157
|
-
|
|
158
|
-
re.compile(pattern) → new RegExp(pattern)
|
|
159
|
-
re.compile(pattern, re.I) → new RegExp(pattern, "i")
|
|
160
|
-
"""
|
|
161
|
-
pattern_expr = jsify(pattern)
|
|
162
|
-
flag_str = ""
|
|
163
|
-
|
|
164
|
-
if isinstance(flags, _FlagExpr):
|
|
165
|
-
flag_str = flags.flag
|
|
166
|
-
elif isinstance(flags, int) and flags != 0:
|
|
167
|
-
# Handle Python flag integers at transpile time
|
|
168
|
-
if flags & re_module.IGNORECASE:
|
|
169
|
-
flag_str += "i"
|
|
170
|
-
if flags & re_module.MULTILINE:
|
|
171
|
-
flag_str += "m"
|
|
172
|
-
if flags & re_module.DOTALL:
|
|
173
|
-
flag_str += "s"
|
|
174
|
-
if flags & re_module.UNICODE:
|
|
175
|
-
flag_str += "u"
|
|
176
|
-
if flags & re_module.VERBOSE:
|
|
177
|
-
raise JSCompilationError(
|
|
178
|
-
"re.VERBOSE flag is not supported - no JavaScript equivalent"
|
|
179
|
-
)
|
|
180
|
-
if flags & re_module.ASCII:
|
|
181
|
-
raise JSCompilationError(
|
|
182
|
-
"re.ASCII flag is not supported in JavaScript RegExp"
|
|
183
|
-
)
|
|
184
|
-
|
|
185
|
-
return _make_regexp(pattern_expr, flag_str)
|
|
186
|
-
|
|
187
|
-
@staticmethod
|
|
188
|
-
def match(
|
|
189
|
-
pattern: str | JSExpr,
|
|
190
|
-
string: str | JSExpr,
|
|
191
|
-
flags: int | JSExpr = 0,
|
|
192
|
-
) -> JSExpr:
|
|
193
|
-
"""Match pattern at the beginning of string.
|
|
194
|
-
|
|
195
|
-
re.match(pattern, string) → string.match(new RegExp("^" + pattern))
|
|
196
|
-
|
|
197
|
-
Returns match array or null.
|
|
198
|
-
"""
|
|
199
|
-
pattern_expr = jsify(pattern)
|
|
200
|
-
string_expr = jsify(string)
|
|
201
|
-
|
|
202
|
-
flag_str = ""
|
|
203
|
-
if isinstance(flags, _FlagExpr):
|
|
204
|
-
flag_str = flags.flag
|
|
205
|
-
elif isinstance(flags, int) and flags != 0:
|
|
206
|
-
if flags & re_module.IGNORECASE:
|
|
207
|
-
flag_str += "i"
|
|
208
|
-
if flags & re_module.MULTILINE:
|
|
209
|
-
flag_str += "m"
|
|
210
|
-
if flags & re_module.DOTALL:
|
|
211
|
-
flag_str += "s"
|
|
212
|
-
|
|
213
|
-
regexp = _make_regexp(pattern_expr, flag_str, anchor_start=True)
|
|
214
|
-
return JSMemberCall(string_expr, "match", [regexp])
|
|
215
|
-
|
|
216
|
-
@staticmethod
|
|
217
|
-
def fullmatch(
|
|
218
|
-
pattern: str | JSExpr,
|
|
219
|
-
string: str | JSExpr,
|
|
220
|
-
flags: int | JSExpr = 0,
|
|
221
|
-
) -> JSExpr:
|
|
222
|
-
"""Match pattern against the entire string.
|
|
223
|
-
|
|
224
|
-
re.fullmatch(pattern, string) → string.match(new RegExp("^pattern$"))
|
|
225
|
-
"""
|
|
226
|
-
pattern_expr = jsify(pattern)
|
|
227
|
-
string_expr = jsify(string)
|
|
228
|
-
|
|
229
|
-
flag_str = ""
|
|
230
|
-
if isinstance(flags, _FlagExpr):
|
|
231
|
-
flag_str = flags.flag
|
|
232
|
-
elif isinstance(flags, int) and flags != 0:
|
|
233
|
-
if flags & re_module.IGNORECASE:
|
|
234
|
-
flag_str += "i"
|
|
235
|
-
if flags & re_module.MULTILINE:
|
|
236
|
-
flag_str += "m"
|
|
237
|
-
if flags & re_module.DOTALL:
|
|
238
|
-
flag_str += "s"
|
|
239
|
-
|
|
240
|
-
regexp = _make_regexp(pattern_expr, flag_str, anchor_both=True)
|
|
241
|
-
return JSMemberCall(string_expr, "match", [regexp])
|
|
242
|
-
|
|
243
|
-
@staticmethod
|
|
244
|
-
def search(
|
|
245
|
-
pattern: str | JSExpr,
|
|
246
|
-
string: str | JSExpr,
|
|
247
|
-
flags: int | JSExpr = 0,
|
|
248
|
-
) -> JSExpr:
|
|
249
|
-
"""Search for pattern anywhere in string.
|
|
250
|
-
|
|
251
|
-
re.search(pattern, string) → new RegExp(pattern).exec(string)
|
|
252
|
-
|
|
253
|
-
Returns match array or null.
|
|
254
|
-
"""
|
|
255
|
-
pattern_expr = jsify(pattern)
|
|
256
|
-
string_expr = jsify(string)
|
|
257
|
-
|
|
258
|
-
flag_str = ""
|
|
259
|
-
if isinstance(flags, _FlagExpr):
|
|
260
|
-
flag_str = flags.flag
|
|
261
|
-
elif isinstance(flags, int) and flags != 0:
|
|
262
|
-
if flags & re_module.IGNORECASE:
|
|
263
|
-
flag_str += "i"
|
|
264
|
-
if flags & re_module.MULTILINE:
|
|
265
|
-
flag_str += "m"
|
|
266
|
-
if flags & re_module.DOTALL:
|
|
267
|
-
flag_str += "s"
|
|
268
|
-
|
|
269
|
-
regexp = _make_regexp(pattern_expr, flag_str)
|
|
270
|
-
return JSMemberCall(regexp, "exec", [string_expr])
|
|
271
|
-
|
|
272
|
-
@staticmethod
|
|
273
|
-
def sub(
|
|
274
|
-
pattern: str | JSExpr,
|
|
275
|
-
repl: str | JSExpr,
|
|
276
|
-
string: str | JSExpr,
|
|
277
|
-
count: int | JSExpr = 0,
|
|
278
|
-
flags: int | JSExpr = 0,
|
|
279
|
-
) -> JSExpr:
|
|
280
|
-
"""Replace occurrences of pattern in string.
|
|
281
|
-
|
|
282
|
-
re.sub(pattern, repl, string) → string.replace(new RegExp(pattern, "g"), repl)
|
|
283
|
-
re.sub(pattern, repl, string, count=1) → string.replace(new RegExp(pattern), repl)
|
|
284
|
-
"""
|
|
285
|
-
pattern_expr = jsify(pattern)
|
|
286
|
-
string_expr = jsify(string)
|
|
287
|
-
repl_expr = jsify(repl)
|
|
288
|
-
|
|
289
|
-
# Convert replacement string if it's a literal
|
|
290
|
-
if isinstance(repl_expr, JSString):
|
|
291
|
-
repl_expr = JSString(_convert_replacement(repl_expr.value))
|
|
292
|
-
|
|
293
|
-
# Determine flags
|
|
294
|
-
flag_str = ""
|
|
295
|
-
if isinstance(flags, _FlagExpr):
|
|
296
|
-
flag_str = flags.flag
|
|
297
|
-
elif isinstance(flags, int) and flags != 0:
|
|
298
|
-
if flags & re_module.IGNORECASE:
|
|
299
|
-
flag_str += "i"
|
|
300
|
-
if flags & re_module.MULTILINE:
|
|
301
|
-
flag_str += "m"
|
|
302
|
-
if flags & re_module.DOTALL:
|
|
303
|
-
flag_str += "s"
|
|
304
|
-
|
|
305
|
-
# Handle count parameter
|
|
306
|
-
use_global = True
|
|
307
|
-
count_val: int | None = None
|
|
308
|
-
if isinstance(count, int):
|
|
309
|
-
count_val = count
|
|
310
|
-
elif isinstance(count, JSNumber) and isinstance(count.value, int):
|
|
311
|
-
count_val = count.value
|
|
312
|
-
|
|
313
|
-
if count_val is not None:
|
|
314
|
-
if count_val == 1:
|
|
315
|
-
use_global = False
|
|
316
|
-
elif count_val > 1:
|
|
317
|
-
raise JSCompilationError(
|
|
318
|
-
"re.sub with count > 1 is not directly supported in JavaScript. Use count=0 (replace all) or count=1 (replace first)."
|
|
319
|
-
)
|
|
320
|
-
elif isinstance(count, JSExpr):
|
|
321
|
-
# Dynamic count - need runtime handling
|
|
322
|
-
raise JSCompilationError(
|
|
323
|
-
"Dynamic count in re.sub is not supported. Use literal 0 or 1."
|
|
324
|
-
)
|
|
325
|
-
|
|
326
|
-
if use_global and "g" not in flag_str:
|
|
327
|
-
flag_str = "g" + flag_str
|
|
328
|
-
|
|
329
|
-
regexp = _make_regexp(pattern_expr, flag_str)
|
|
330
|
-
return JSMemberCall(string_expr, "replace", [regexp, repl_expr])
|
|
331
|
-
|
|
332
|
-
@staticmethod
|
|
333
|
-
def split(
|
|
334
|
-
pattern: str | JSExpr,
|
|
335
|
-
string: str | JSExpr,
|
|
336
|
-
maxsplit: int | JSExpr = 0,
|
|
337
|
-
flags: int | JSExpr = 0,
|
|
338
|
-
) -> JSExpr:
|
|
339
|
-
"""Split string by pattern.
|
|
340
|
-
|
|
341
|
-
re.split(pattern, string) → string.split(new RegExp(pattern))
|
|
342
|
-
|
|
343
|
-
Note: maxsplit > 0 requires runtime handling and is limited.
|
|
344
|
-
"""
|
|
345
|
-
pattern_expr = jsify(pattern)
|
|
346
|
-
string_expr = jsify(string)
|
|
347
|
-
|
|
348
|
-
flag_str = ""
|
|
349
|
-
if isinstance(flags, _FlagExpr):
|
|
350
|
-
flag_str = flags.flag
|
|
351
|
-
elif isinstance(flags, int) and flags != 0:
|
|
352
|
-
if flags & re_module.IGNORECASE:
|
|
353
|
-
flag_str += "i"
|
|
354
|
-
if flags & re_module.MULTILINE:
|
|
355
|
-
flag_str += "m"
|
|
356
|
-
if flags & re_module.DOTALL:
|
|
357
|
-
flag_str += "s"
|
|
358
|
-
|
|
359
|
-
regexp = _make_regexp(pattern_expr, flag_str)
|
|
360
|
-
|
|
361
|
-
# Extract maxsplit value
|
|
362
|
-
maxsplit_val: int | None = None
|
|
363
|
-
if isinstance(maxsplit, int):
|
|
364
|
-
maxsplit_val = maxsplit
|
|
365
|
-
elif isinstance(maxsplit, JSNumber) and isinstance(maxsplit.value, int):
|
|
366
|
-
maxsplit_val = maxsplit.value
|
|
367
|
-
|
|
368
|
-
if maxsplit_val is not None:
|
|
369
|
-
if maxsplit_val == 0:
|
|
370
|
-
# No limit
|
|
371
|
-
return JSMemberCall(string_expr, "split", [regexp])
|
|
372
|
-
elif maxsplit_val > 0:
|
|
373
|
-
# JS split limit is different from Python's maxsplit
|
|
374
|
-
# Python: maxsplit=2 means at most 2 splits, resulting in 3 parts
|
|
375
|
-
# JS: limit=3 means at most 3 parts
|
|
376
|
-
# So we add 1 to convert
|
|
377
|
-
return JSMemberCall(
|
|
378
|
-
string_expr, "split", [regexp, JSNumber(maxsplit_val + 1)]
|
|
379
|
-
)
|
|
380
|
-
|
|
381
|
-
raise JSCompilationError(
|
|
382
|
-
"Dynamic maxsplit in re.split is not supported. Use literal value."
|
|
383
|
-
)
|
|
384
|
-
|
|
385
|
-
@staticmethod
|
|
386
|
-
def findall(
|
|
387
|
-
pattern: str | JSExpr,
|
|
388
|
-
string: str | JSExpr,
|
|
389
|
-
flags: int | JSExpr = 0,
|
|
390
|
-
) -> JSExpr:
|
|
391
|
-
"""Find all matches of pattern in string.
|
|
392
|
-
|
|
393
|
-
re.findall(pattern, string) → [...string.matchAll(new RegExp(pattern, "g"))].map(m => m[0])
|
|
394
|
-
|
|
395
|
-
Note: Returns array of matched strings (not groups).
|
|
396
|
-
For patterns with groups, this returns the full match, not the groups.
|
|
397
|
-
"""
|
|
398
|
-
pattern_expr = jsify(pattern)
|
|
399
|
-
string_expr = jsify(string)
|
|
400
|
-
|
|
401
|
-
flag_str = "g" # Always need global for matchAll
|
|
402
|
-
if isinstance(flags, _FlagExpr):
|
|
403
|
-
flag_str += flags.flag
|
|
404
|
-
elif isinstance(flags, int) and flags != 0:
|
|
405
|
-
if flags & re_module.IGNORECASE:
|
|
406
|
-
flag_str += "i"
|
|
407
|
-
if flags & re_module.MULTILINE:
|
|
408
|
-
flag_str += "m"
|
|
409
|
-
if flags & re_module.DOTALL:
|
|
410
|
-
flag_str += "s"
|
|
411
|
-
|
|
412
|
-
regexp = _make_regexp(pattern_expr, flag_str)
|
|
413
|
-
|
|
414
|
-
# [...string.matchAll(regexp)].map(m => m[0])
|
|
415
|
-
match_all = JSMemberCall(string_expr, "matchAll", [regexp])
|
|
416
|
-
spread_array = JSArray([JSSpread(match_all)])
|
|
417
|
-
# Arrow: m => m[0]
|
|
418
|
-
arrow = JSArrowFunction("m", JSSubscript(JSIdentifier("m"), JSNumber(0)))
|
|
419
|
-
return JSMemberCall(spread_array, "map", [arrow])
|
|
420
|
-
|
|
421
|
-
@staticmethod
|
|
422
|
-
def test(
|
|
423
|
-
pattern: str | JSExpr,
|
|
424
|
-
string: str | JSExpr,
|
|
425
|
-
flags: int | JSExpr = 0,
|
|
426
|
-
) -> JSExpr:
|
|
427
|
-
"""Test if pattern matches anywhere in string.
|
|
428
|
-
|
|
429
|
-
This is a convenience method (not in Python's re module) that maps to RegExp.test().
|
|
430
|
-
|
|
431
|
-
re.test(pattern, string) → new RegExp(pattern).test(string)
|
|
432
|
-
|
|
433
|
-
Returns boolean.
|
|
434
|
-
"""
|
|
435
|
-
pattern_expr = jsify(pattern)
|
|
436
|
-
string_expr = jsify(string)
|
|
437
|
-
|
|
438
|
-
flag_str = ""
|
|
439
|
-
if isinstance(flags, _FlagExpr):
|
|
440
|
-
flag_str = flags.flag
|
|
441
|
-
elif isinstance(flags, int) and flags != 0:
|
|
442
|
-
if flags & re_module.IGNORECASE:
|
|
443
|
-
flag_str += "i"
|
|
444
|
-
if flags & re_module.MULTILINE:
|
|
445
|
-
flag_str += "m"
|
|
446
|
-
if flags & re_module.DOTALL:
|
|
447
|
-
flag_str += "s"
|
|
448
|
-
|
|
449
|
-
regexp = _make_regexp(pattern_expr, flag_str)
|
|
450
|
-
return JSMemberCall(regexp, "test", [string_expr])
|
|
451
|
-
|
|
452
|
-
@staticmethod
|
|
453
|
-
def escape(pattern: str | JSExpr) -> JSExpr:
|
|
454
|
-
"""Escape special regex characters in pattern.
|
|
455
|
-
|
|
456
|
-
re.escape(string) → string.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\\\$&")
|
|
457
|
-
"""
|
|
458
|
-
pattern_expr = jsify(pattern)
|
|
459
|
-
|
|
460
|
-
# The escape regex pattern for JS
|
|
461
|
-
escape_regexp = JSNew(
|
|
462
|
-
JSIdentifier("RegExp"),
|
|
463
|
-
[JSString("[.*+?^${}()|[\\]\\\\]"), JSString("g")],
|
|
464
|
-
)
|
|
465
|
-
|
|
466
|
-
return JSMemberCall(pattern_expr, "replace", [escape_regexp, JSString("\\$&")])
|