pulse-framework 0.1.46__py3-none-any.whl → 0.1.47__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 +9 -23
- pulse/app.py +2 -24
- pulse/codegen/codegen.py +43 -88
- pulse/codegen/js.py +35 -5
- pulse/codegen/templates/route.py +341 -254
- pulse/form.py +1 -1
- pulse/helpers.py +40 -8
- pulse/hooks/core.py +2 -2
- pulse/hooks/effects.py +1 -1
- pulse/hooks/init.py +2 -1
- pulse/hooks/setup.py +1 -1
- pulse/hooks/stable.py +2 -2
- pulse/hooks/states.py +2 -2
- pulse/html/props.py +3 -2
- pulse/html/tags.py +135 -0
- pulse/html/tags.pyi +4 -0
- pulse/js/__init__.py +110 -0
- pulse/js/__init__.pyi +95 -0
- pulse/js/_types.py +297 -0
- pulse/js/array.py +253 -0
- pulse/js/console.py +47 -0
- pulse/js/date.py +113 -0
- pulse/js/document.py +138 -0
- pulse/js/error.py +139 -0
- pulse/js/json.py +62 -0
- pulse/js/map.py +84 -0
- pulse/js/math.py +66 -0
- pulse/js/navigator.py +76 -0
- pulse/js/number.py +54 -0
- pulse/js/object.py +173 -0
- pulse/js/promise.py +150 -0
- pulse/js/regexp.py +54 -0
- pulse/js/set.py +109 -0
- pulse/js/string.py +35 -0
- pulse/js/weakmap.py +50 -0
- pulse/js/weakset.py +45 -0
- pulse/js/window.py +199 -0
- pulse/messages.py +22 -3
- pulse/react_component.py +167 -14
- pulse/reactive_extensions.py +5 -5
- pulse/render_session.py +144 -34
- pulse/renderer.py +80 -115
- pulse/routing.py +1 -18
- pulse/transpiler/__init__.py +131 -0
- pulse/transpiler/builtins.py +731 -0
- pulse/transpiler/constants.py +110 -0
- pulse/transpiler/context.py +26 -0
- pulse/transpiler/errors.py +2 -0
- pulse/transpiler/function.py +250 -0
- pulse/transpiler/ids.py +16 -0
- pulse/transpiler/imports.py +409 -0
- pulse/transpiler/js_module.py +274 -0
- pulse/transpiler/modules/__init__.py +30 -0
- pulse/transpiler/modules/asyncio.py +38 -0
- pulse/transpiler/modules/json.py +20 -0
- pulse/transpiler/modules/math.py +320 -0
- pulse/transpiler/modules/re.py +466 -0
- pulse/transpiler/modules/tags.py +268 -0
- pulse/transpiler/modules/typing.py +59 -0
- pulse/transpiler/nodes.py +1216 -0
- pulse/transpiler/py_module.py +119 -0
- pulse/transpiler/transpiler.py +938 -0
- pulse/transpiler/utils.py +4 -0
- pulse/vdom.py +112 -6
- {pulse_framework-0.1.46.dist-info → pulse_framework-0.1.47.dist-info}/METADATA +1 -1
- pulse_framework-0.1.47.dist-info/RECORD +119 -0
- pulse/codegen/imports.py +0 -204
- pulse/css.py +0 -155
- pulse_framework-0.1.46.dist-info/RECORD +0 -80
- {pulse_framework-0.1.46.dist-info → pulse_framework-0.1.47.dist-info}/WHEEL +0 -0
- {pulse_framework-0.1.46.dist-info → pulse_framework-0.1.47.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Python asyncio module transpilation to JavaScript Promise operations.
|
|
2
|
+
|
|
3
|
+
This module provides transpilation from Python's `asyncio` module to JavaScript's `Promise` methods.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from pulse.transpiler.nodes import JSArray, JSExpr, JSIdentifier, JSMemberCall
|
|
7
|
+
from pulse.transpiler.py_module import PyModule
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class PyAsyncio(PyModule):
|
|
11
|
+
"""Provides transpilation for Python asyncio functions to JavaScript Promise methods."""
|
|
12
|
+
|
|
13
|
+
@staticmethod
|
|
14
|
+
def gather(*coros: JSExpr, **kwargs: JSExpr) -> JSExpr:
|
|
15
|
+
"""Transpile asyncio.gather to Promise.all or Promise.allSettled.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
*coros: Variable number of coroutine/promise expressions
|
|
19
|
+
**kwargs: Keyword arguments, including return_exceptions
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
JSExpr representing Promise.all([...]) or Promise.allSettled([...])
|
|
23
|
+
"""
|
|
24
|
+
# Convert coros to array
|
|
25
|
+
promises = JSArray(list(coros))
|
|
26
|
+
|
|
27
|
+
# Check return_exceptions keyword argument
|
|
28
|
+
return_exceptions = kwargs.get("return_exceptions")
|
|
29
|
+
if return_exceptions is not None:
|
|
30
|
+
# Check if it's a boolean true (JSBoolean(True))
|
|
31
|
+
from pulse.transpiler.nodes import JSBoolean
|
|
32
|
+
|
|
33
|
+
if isinstance(return_exceptions, JSBoolean) and return_exceptions.value:
|
|
34
|
+
# Promise.allSettled returns results with status
|
|
35
|
+
return JSMemberCall(JSIdentifier("Promise"), "allSettled", [promises])
|
|
36
|
+
|
|
37
|
+
# Default: Promise.all fails fast on first rejection
|
|
38
|
+
return JSMemberCall(JSIdentifier("Promise"), "all", [promises])
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""Python json module transpilation to JavaScript JSON."""
|
|
2
|
+
|
|
3
|
+
from pulse.transpiler.nodes import JSExpr, JSIdentifier, JSMemberCall
|
|
4
|
+
from pulse.transpiler.py_module import PyModule
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def JSONCall(name: str, *args: JSExpr) -> JSExpr:
|
|
8
|
+
return JSMemberCall(JSIdentifier("JSON"), name, list(args))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class PyJson(PyModule):
|
|
12
|
+
"""Provides transpilation for Python json functions to JavaScript."""
|
|
13
|
+
|
|
14
|
+
@staticmethod
|
|
15
|
+
def dumps(obj: JSExpr) -> JSExpr:
|
|
16
|
+
return JSONCall("stringify", obj)
|
|
17
|
+
|
|
18
|
+
@staticmethod
|
|
19
|
+
def loads(s: JSExpr) -> JSExpr:
|
|
20
|
+
return JSONCall("parse", s)
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
"""Python math module transpilation to JavaScript Math.
|
|
2
|
+
|
|
3
|
+
This module provides transpilation from Python's `math` module to JavaScript's `Math` object.
|
|
4
|
+
For direct JavaScript Math bindings, use `pulse.js.math` instead.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
# pyright: reportUnannotatedClassAttribute=false
|
|
8
|
+
|
|
9
|
+
from pulse.transpiler.constants import jsify
|
|
10
|
+
from pulse.transpiler.nodes import (
|
|
11
|
+
JSBinary,
|
|
12
|
+
JSExpr,
|
|
13
|
+
JSIdentifier,
|
|
14
|
+
JSMember,
|
|
15
|
+
JSMemberCall,
|
|
16
|
+
JSNumber,
|
|
17
|
+
JSUnary,
|
|
18
|
+
)
|
|
19
|
+
from pulse.transpiler.py_module import PyModule
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# Helper for generating Math method calls during transpilation
|
|
23
|
+
def MathCall(name: str, *args: int | float | JSExpr) -> JSExpr:
|
|
24
|
+
return JSMemberCall(JSIdentifier("Math"), name, [jsify(a) for a in args])
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def MathProp(name: str) -> JSExpr:
|
|
28
|
+
return JSMember(JSIdentifier("Math"), name)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class PyMath(PyModule):
|
|
32
|
+
"""Provides transpilation for Python math functions to JavaScript."""
|
|
33
|
+
|
|
34
|
+
# Constants (as class attributes returning JSExpr)
|
|
35
|
+
pi = MathProp("PI")
|
|
36
|
+
e = MathProp("E")
|
|
37
|
+
tau = JSBinary(JSNumber(2), "*", MathProp("PI")) # 2 * PI
|
|
38
|
+
inf = JSIdentifier("Infinity")
|
|
39
|
+
nan = JSIdentifier("NaN")
|
|
40
|
+
|
|
41
|
+
@staticmethod
|
|
42
|
+
def acos(x: int | float | JSExpr) -> JSExpr:
|
|
43
|
+
return MathCall("acos", x)
|
|
44
|
+
|
|
45
|
+
@staticmethod
|
|
46
|
+
def acosh(x: int | float | JSExpr) -> JSExpr:
|
|
47
|
+
return MathCall("acosh", x)
|
|
48
|
+
|
|
49
|
+
@staticmethod
|
|
50
|
+
def asin(x: int | float | JSExpr) -> JSExpr:
|
|
51
|
+
return MathCall("asin", x)
|
|
52
|
+
|
|
53
|
+
@staticmethod
|
|
54
|
+
def asinh(x: int | float | JSExpr) -> JSExpr:
|
|
55
|
+
return MathCall("asinh", x)
|
|
56
|
+
|
|
57
|
+
@staticmethod
|
|
58
|
+
def atan(x: int | float | JSExpr) -> JSExpr:
|
|
59
|
+
return MathCall("atan", x)
|
|
60
|
+
|
|
61
|
+
@staticmethod
|
|
62
|
+
def atan2(y: int | float | JSExpr, x: int | float | JSExpr) -> JSExpr:
|
|
63
|
+
return MathCall("atan2", y, x)
|
|
64
|
+
|
|
65
|
+
@staticmethod
|
|
66
|
+
def atanh(x: int | float | JSExpr) -> JSExpr:
|
|
67
|
+
return MathCall("atanh", x)
|
|
68
|
+
|
|
69
|
+
@staticmethod
|
|
70
|
+
def cbrt(x: int | float | JSExpr) -> JSExpr:
|
|
71
|
+
return MathCall("cbrt", x)
|
|
72
|
+
|
|
73
|
+
@staticmethod
|
|
74
|
+
def ceil(x: int | float | JSExpr) -> JSExpr:
|
|
75
|
+
return MathCall("ceil", x)
|
|
76
|
+
|
|
77
|
+
@staticmethod
|
|
78
|
+
def copysign(x: int | float | JSExpr, y: int | float | JSExpr) -> JSExpr:
|
|
79
|
+
# Math.sign(y) * Math.abs(x)
|
|
80
|
+
return JSBinary(MathCall("sign", y), "*", MathCall("abs", x))
|
|
81
|
+
|
|
82
|
+
@staticmethod
|
|
83
|
+
def cos(x: int | float | JSExpr) -> JSExpr:
|
|
84
|
+
return MathCall("cos", x)
|
|
85
|
+
|
|
86
|
+
@staticmethod
|
|
87
|
+
def cosh(x: int | float | JSExpr) -> JSExpr:
|
|
88
|
+
return MathCall("cosh", x)
|
|
89
|
+
|
|
90
|
+
@staticmethod
|
|
91
|
+
def degrees(x: int | float | JSExpr) -> JSExpr:
|
|
92
|
+
# Convert radians to degrees: x * (180 / π)
|
|
93
|
+
return JSBinary(jsify(x), "*", JSBinary(JSNumber(180), "/", MathProp("PI")))
|
|
94
|
+
|
|
95
|
+
@staticmethod
|
|
96
|
+
def dist(
|
|
97
|
+
p: int | float | JSExpr | list[int | float | JSExpr],
|
|
98
|
+
q: int | float | JSExpr | list[int | float | JSExpr],
|
|
99
|
+
) -> JSExpr:
|
|
100
|
+
raise NotImplementedError("dist requires array/iterable handling")
|
|
101
|
+
|
|
102
|
+
@staticmethod
|
|
103
|
+
def erf(x: int | float | JSExpr) -> JSExpr:
|
|
104
|
+
raise NotImplementedError("erf requires special function implementation")
|
|
105
|
+
|
|
106
|
+
@staticmethod
|
|
107
|
+
def erfc(x: int | float | JSExpr) -> JSExpr:
|
|
108
|
+
raise NotImplementedError("erfc requires special function implementation")
|
|
109
|
+
|
|
110
|
+
@staticmethod
|
|
111
|
+
def exp(x: int | float | JSExpr) -> JSExpr:
|
|
112
|
+
return MathCall("exp", x)
|
|
113
|
+
|
|
114
|
+
@staticmethod
|
|
115
|
+
def exp2(x: int | float | JSExpr) -> JSExpr:
|
|
116
|
+
# 2 ** x
|
|
117
|
+
return JSBinary(JSNumber(2), "**", jsify(x))
|
|
118
|
+
|
|
119
|
+
@staticmethod
|
|
120
|
+
def expm1(x: int | float | JSExpr) -> JSExpr:
|
|
121
|
+
return MathCall("expm1", x)
|
|
122
|
+
|
|
123
|
+
@staticmethod
|
|
124
|
+
def fabs(x: int | float | JSExpr) -> JSExpr:
|
|
125
|
+
return MathCall("abs", x)
|
|
126
|
+
|
|
127
|
+
@staticmethod
|
|
128
|
+
def factorial(x: int | float | JSExpr) -> JSExpr:
|
|
129
|
+
raise NotImplementedError("factorial requires iterative implementation")
|
|
130
|
+
|
|
131
|
+
@staticmethod
|
|
132
|
+
def floor(x: int | float | JSExpr) -> JSExpr:
|
|
133
|
+
return MathCall("floor", x)
|
|
134
|
+
|
|
135
|
+
@staticmethod
|
|
136
|
+
def fmod(x: int | float | JSExpr, y: int | float | JSExpr) -> JSExpr:
|
|
137
|
+
# JavaScript % operator matches Python fmod for most cases
|
|
138
|
+
return JSBinary(jsify(x), "%", jsify(y))
|
|
139
|
+
|
|
140
|
+
@staticmethod
|
|
141
|
+
def frexp(x: int | float | JSExpr) -> JSExpr:
|
|
142
|
+
raise NotImplementedError("frexp returns tuple, requires special handling")
|
|
143
|
+
|
|
144
|
+
@staticmethod
|
|
145
|
+
def fsum(seq: int | float | JSExpr | list[int | float | JSExpr]) -> JSExpr:
|
|
146
|
+
raise NotImplementedError("fsum requires iterable handling")
|
|
147
|
+
|
|
148
|
+
@staticmethod
|
|
149
|
+
def gamma(x: int | float | JSExpr) -> JSExpr:
|
|
150
|
+
raise NotImplementedError("gamma requires special function implementation")
|
|
151
|
+
|
|
152
|
+
@staticmethod
|
|
153
|
+
def gcd(*integers: int | float | JSExpr) -> JSExpr:
|
|
154
|
+
raise NotImplementedError("gcd requires iterative implementation")
|
|
155
|
+
|
|
156
|
+
@staticmethod
|
|
157
|
+
def hypot(*coordinates: int | float | JSExpr) -> JSExpr:
|
|
158
|
+
return MathCall("hypot", *coordinates)
|
|
159
|
+
|
|
160
|
+
@staticmethod
|
|
161
|
+
def isclose(
|
|
162
|
+
a: int | float | JSExpr,
|
|
163
|
+
b: int | float | JSExpr,
|
|
164
|
+
*,
|
|
165
|
+
rel_tol: int | float | JSExpr = 1e-09,
|
|
166
|
+
abs_tol: int | float | JSExpr = 0.0,
|
|
167
|
+
) -> JSExpr:
|
|
168
|
+
# abs(a - b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)
|
|
169
|
+
abs_diff = MathCall("abs", JSBinary(jsify(a), "-", jsify(b)))
|
|
170
|
+
max_abs = JSMemberCall(
|
|
171
|
+
JSIdentifier("Math"),
|
|
172
|
+
"max",
|
|
173
|
+
[MathCall("abs", a), MathCall("abs", b)],
|
|
174
|
+
)
|
|
175
|
+
rel_bound = JSBinary(jsify(rel_tol), "*", max_abs)
|
|
176
|
+
max_bound = JSMemberCall(
|
|
177
|
+
JSIdentifier("Math"), "max", [rel_bound, jsify(abs_tol)]
|
|
178
|
+
)
|
|
179
|
+
return JSBinary(abs_diff, "<=", max_bound)
|
|
180
|
+
|
|
181
|
+
@staticmethod
|
|
182
|
+
def isfinite(x: int | float | JSExpr) -> JSExpr:
|
|
183
|
+
return JSMemberCall(JSIdentifier("Number"), "isFinite", [jsify(x)])
|
|
184
|
+
|
|
185
|
+
@staticmethod
|
|
186
|
+
def isinf(x: int | float | JSExpr) -> JSExpr:
|
|
187
|
+
is_finite = JSMemberCall(JSIdentifier("Number"), "isFinite", [jsify(x)])
|
|
188
|
+
is_nan = JSMemberCall(JSIdentifier("Number"), "isNaN", [jsify(x)])
|
|
189
|
+
return JSBinary(JSUnary("!", is_finite), "&&", JSUnary("!", is_nan))
|
|
190
|
+
|
|
191
|
+
@staticmethod
|
|
192
|
+
def isnan(x: int | float | JSExpr) -> JSExpr:
|
|
193
|
+
return JSMemberCall(JSIdentifier("Number"), "isNaN", [jsify(x)])
|
|
194
|
+
|
|
195
|
+
@staticmethod
|
|
196
|
+
def isqrt(n: int | float | JSExpr) -> JSExpr:
|
|
197
|
+
return MathCall("floor", MathCall("sqrt", n))
|
|
198
|
+
|
|
199
|
+
@staticmethod
|
|
200
|
+
def lcm(*integers: int | float | JSExpr) -> JSExpr:
|
|
201
|
+
raise NotImplementedError("lcm requires iterative implementation")
|
|
202
|
+
|
|
203
|
+
@staticmethod
|
|
204
|
+
def ldexp(x: int | float | JSExpr, i: int | float | JSExpr) -> JSExpr:
|
|
205
|
+
# x * (2 ** i)
|
|
206
|
+
return JSBinary(jsify(x), "*", JSBinary(JSNumber(2), "**", jsify(i)))
|
|
207
|
+
|
|
208
|
+
@staticmethod
|
|
209
|
+
def lgamma(x: int | float | JSExpr) -> JSExpr:
|
|
210
|
+
raise NotImplementedError("lgamma requires special function implementation")
|
|
211
|
+
|
|
212
|
+
@staticmethod
|
|
213
|
+
def log(
|
|
214
|
+
value: int | float | JSExpr,
|
|
215
|
+
base: int | float | JSExpr | None = None,
|
|
216
|
+
) -> JSExpr:
|
|
217
|
+
if base is None:
|
|
218
|
+
return MathCall("log", value)
|
|
219
|
+
return JSBinary(MathCall("log", value), "/", MathCall("log", base))
|
|
220
|
+
|
|
221
|
+
@staticmethod
|
|
222
|
+
def log10(x: int | float | JSExpr) -> JSExpr:
|
|
223
|
+
return MathCall("log10", x)
|
|
224
|
+
|
|
225
|
+
@staticmethod
|
|
226
|
+
def log1p(x: int | float | JSExpr) -> JSExpr:
|
|
227
|
+
return MathCall("log1p", x)
|
|
228
|
+
|
|
229
|
+
@staticmethod
|
|
230
|
+
def log2(x: int | float | JSExpr) -> JSExpr:
|
|
231
|
+
return MathCall("log2", x)
|
|
232
|
+
|
|
233
|
+
@staticmethod
|
|
234
|
+
def modf(x: int | float | JSExpr) -> JSExpr:
|
|
235
|
+
raise NotImplementedError("modf returns tuple, requires special handling")
|
|
236
|
+
|
|
237
|
+
@staticmethod
|
|
238
|
+
def nextafter(
|
|
239
|
+
x: int | float | JSExpr,
|
|
240
|
+
y: int | float | JSExpr,
|
|
241
|
+
*,
|
|
242
|
+
steps: int | float | JSExpr | None = None,
|
|
243
|
+
) -> JSExpr:
|
|
244
|
+
raise NotImplementedError("nextafter requires special implementation")
|
|
245
|
+
|
|
246
|
+
@staticmethod
|
|
247
|
+
def perm(n: int | float | JSExpr, k: int | float | JSExpr | None = None) -> JSExpr:
|
|
248
|
+
raise NotImplementedError("perm requires factorial implementation")
|
|
249
|
+
|
|
250
|
+
@staticmethod
|
|
251
|
+
def pow(x: int | float | JSExpr, y: int | float | JSExpr) -> JSExpr:
|
|
252
|
+
return MathCall("pow", x, y)
|
|
253
|
+
|
|
254
|
+
@staticmethod
|
|
255
|
+
def prod(
|
|
256
|
+
iterable: int | float | JSExpr | list[int | float | JSExpr],
|
|
257
|
+
*,
|
|
258
|
+
start: int | float | JSExpr = 1,
|
|
259
|
+
) -> JSExpr:
|
|
260
|
+
raise NotImplementedError("prod requires iterable handling")
|
|
261
|
+
|
|
262
|
+
@staticmethod
|
|
263
|
+
def radians(x: int | float | JSExpr) -> JSExpr:
|
|
264
|
+
# Convert degrees to radians: x * (π / 180)
|
|
265
|
+
return JSBinary(jsify(x), "*", JSBinary(MathProp("PI"), "/", JSNumber(180)))
|
|
266
|
+
|
|
267
|
+
@staticmethod
|
|
268
|
+
def remainder(x: int | float | JSExpr, y: int | float | JSExpr) -> JSExpr:
|
|
269
|
+
# x - n * y where n is the nearest integer to x/y
|
|
270
|
+
n = MathCall("round", JSBinary(jsify(x), "/", jsify(y)))
|
|
271
|
+
return JSBinary(jsify(x), "-", JSBinary(n, "*", jsify(y)))
|
|
272
|
+
|
|
273
|
+
@staticmethod
|
|
274
|
+
def sin(x: int | float | JSExpr) -> JSExpr:
|
|
275
|
+
return MathCall("sin", x)
|
|
276
|
+
|
|
277
|
+
@staticmethod
|
|
278
|
+
def sinh(x: int | float | JSExpr) -> JSExpr:
|
|
279
|
+
return MathCall("sinh", x)
|
|
280
|
+
|
|
281
|
+
@staticmethod
|
|
282
|
+
def sqrt(x: int | float | JSExpr) -> JSExpr:
|
|
283
|
+
return MathCall("sqrt", x)
|
|
284
|
+
|
|
285
|
+
@staticmethod
|
|
286
|
+
def sumprod(
|
|
287
|
+
p: int | float | JSExpr | list[int | float | JSExpr],
|
|
288
|
+
q: int | float | JSExpr | list[int | float | JSExpr],
|
|
289
|
+
) -> JSExpr:
|
|
290
|
+
raise NotImplementedError("sumprod requires iterable handling")
|
|
291
|
+
|
|
292
|
+
@staticmethod
|
|
293
|
+
def tan(x: int | float | JSExpr) -> JSExpr:
|
|
294
|
+
return MathCall("tan", x)
|
|
295
|
+
|
|
296
|
+
@staticmethod
|
|
297
|
+
def tanh(x: int | float | JSExpr) -> JSExpr:
|
|
298
|
+
return MathCall("tanh", x)
|
|
299
|
+
|
|
300
|
+
@staticmethod
|
|
301
|
+
def trunc(x: int | float | JSExpr) -> JSExpr:
|
|
302
|
+
return MathCall("trunc", x)
|
|
303
|
+
|
|
304
|
+
@staticmethod
|
|
305
|
+
def ulp(x: int | float | JSExpr) -> JSExpr:
|
|
306
|
+
raise NotImplementedError("ulp requires special implementation")
|
|
307
|
+
|
|
308
|
+
@staticmethod
|
|
309
|
+
def fma(
|
|
310
|
+
x: int | float | JSExpr,
|
|
311
|
+
y: int | float | JSExpr,
|
|
312
|
+
z: int | float | JSExpr,
|
|
313
|
+
) -> JSExpr:
|
|
314
|
+
# Fused multiply-add: (x * y) + z (with single rounding)
|
|
315
|
+
# JavaScript doesn't have native fma, so we just do the operation
|
|
316
|
+
return JSBinary(JSBinary(jsify(x), "*", jsify(y)), "+", jsify(z))
|
|
317
|
+
|
|
318
|
+
@staticmethod
|
|
319
|
+
def comb(n: int | float | JSExpr, k: int | float | JSExpr) -> JSExpr:
|
|
320
|
+
raise NotImplementedError("comb requires factorial implementation")
|