@kernlang/python 4.0.0 → 4.0.1-canary.224.1.1a92ac0a
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.
- package/dist/codegen-body-python.d.ts +13 -0
- package/dist/codegen-body-python.js +1140 -145
- package/dist/codegen-body-python.js.map +1 -1
- package/dist/core/emit-models.js +5 -1
- package/dist/core/emit-models.js.map +1 -1
- package/dist/core/expr/helpers.d.ts +45 -0
- package/dist/core/expr/helpers.js +509 -5
- package/dist/core/expr/helpers.js.map +1 -1
- package/dist/core/expr/index.d.ts +1 -1
- package/dist/core/expr/index.js +137 -9
- package/dist/core/expr/index.js.map +1 -1
- package/dist/core/expr/list-ops.d.ts +10 -1
- package/dist/core/expr/list-ops.js +5 -9
- package/dist/core/expr/list-ops.js.map +1 -1
- package/dist/generators/core.js +5 -1
- package/dist/generators/core.js.map +1 -1
- package/dist/generators/ground.d.ts +13 -0
- package/dist/generators/ground.js +117 -12
- package/dist/generators/ground.js.map +1 -1
- package/package.json +2 -2
package/dist/core/emit-models.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { generatePythonCoreNode } from '../codegen-python.js';
|
|
2
|
+
import { dedupeGroundPrelude } from '../generators/ground.js';
|
|
2
3
|
function findServerNode(root) {
|
|
3
4
|
if (root.type === 'server')
|
|
4
5
|
return root;
|
|
@@ -78,8 +79,11 @@ export function emitModels(root, options) {
|
|
|
78
79
|
lines.push('');
|
|
79
80
|
}
|
|
80
81
|
}
|
|
82
|
+
// Top-level ground statements inline their helper/import prelude per-statement
|
|
83
|
+
// (a `module` wrapper dedupes in generateModule); collapse repeats across the
|
|
84
|
+
// assembled top-level bodies so each helper block appears once in the module.
|
|
81
85
|
return {
|
|
82
|
-
code: lines.join('\n'),
|
|
86
|
+
code: dedupeGroundPrelude(lines).join('\n'),
|
|
83
87
|
bodies,
|
|
84
88
|
};
|
|
85
89
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"emit-models.js","sourceRoot":"","sources":["../../src/core/emit-models.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"emit-models.js","sourceRoot":"","sources":["../../src/core/emit-models.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,53 @@
|
|
|
1
|
+
export declare const KERN_NULLISH_HELPER_PY: string;
|
|
2
|
+
export declare const KERN_JSON_STRINGIFY_SHIM_PY: string;
|
|
1
3
|
export declare const KERN_PAIR_HELPERS_PY: string;
|
|
2
4
|
export declare const KERN_FMT_HELPER_PY: string;
|
|
3
5
|
export declare const KERN_I32_HELPER_PY: string;
|
|
6
|
+
/**
|
|
7
|
+
* ToNumericPrimitive substrate (slice 0.75) — Python twin of the
|
|
8
|
+
* `@kernlang/core` `to-numeric` decision kernel.
|
|
9
|
+
*
|
|
10
|
+
* Single Python-side source of numeric coercion truth for the FROZEN primitive
|
|
11
|
+
* domain (numbers, ECMA numeric strings, booleans, null, undefined sentinel).
|
|
12
|
+
* The emitted Python encodes the ECMA-262 StringNumericLiteral grammar
|
|
13
|
+
* EXPLICITLY rather than delegating to `float(...)`, because `float()` diverges
|
|
14
|
+
* from JS `Number()` on three load-bearing cases: it accepts numeric separators
|
|
15
|
+
* (`float('1_000') == 1000.0`), case-insensitive infinity/NaN words
|
|
16
|
+
* (`float('infinity')`, `float('nan')`), and raises on `0x`/`0b`/`0o` prefixes.
|
|
17
|
+
*
|
|
18
|
+
* Return-type contract (tribunal amendments 1 & 2):
|
|
19
|
+
* - `_kern_to_number(x)` -> Python `float` for EVERY numeric output
|
|
20
|
+
* (bool/null/hex/binary/octal inputs included); `-0.0` sign survives; NaN
|
|
21
|
+
* and ±inf preserved.
|
|
22
|
+
* - `_kern_string_to_number(text)` -> `float` (NaN for any non-grammar string).
|
|
23
|
+
* - `_kern_number_to_int32(n)` -> `int` (signed 32-bit, shift-mask domain).
|
|
24
|
+
* - `_kern_number_to_uint32(n)` -> `int` (unsigned 32-bit).
|
|
25
|
+
* - `_kern_to_int32(x)` -> `int` = int32(to_number(x)).
|
|
26
|
+
* - `_kern_to_uint32(x)` -> `int` = uint32(to_number(x)).
|
|
27
|
+
* - `_kern_to_integer_or_infinity(x)` -> `float` (int-valued, or ±inf).
|
|
28
|
+
*
|
|
29
|
+
* Fail-closed: objects/arrays/functions/symbols/custom-valueOf RAISE
|
|
30
|
+
* `_KernNumericCoercionError` (the caller decides) — full ToPrimitive deferred.
|
|
31
|
+
*
|
|
32
|
+
* Single-source-of-int32 note: this block is the coercion-correct int32 path
|
|
33
|
+
* going forward. The legacy `_i32` (KERN_I32_HELPER_PY) embeds its OWN
|
|
34
|
+
* float()-based coercion and is still wired to production
|
|
35
|
+
* (codegen-body-python.ts ToInt32 lowering). Slice 0.75 is a PURE ADDITION —
|
|
36
|
+
* nothing is rerouted here, so both coexist; the future routing slice retires
|
|
37
|
+
* `_i32` in favor of `_kern_to_int32` once the fallback count hits zero.
|
|
38
|
+
*
|
|
39
|
+
* The ECMA whitespace string below is the exact `StrWhiteSpace` set
|
|
40
|
+
* (`WhiteSpace` + `LineTerminator`) JS trims from a StringNumericLiteral, kept
|
|
41
|
+
* byte-aligned with `ECMA_STR_WHITESPACE` in the TS kernel.
|
|
42
|
+
*/
|
|
43
|
+
export declare const KERN_TO_NUMBER_HELPER_PY: string;
|
|
4
44
|
export declare const KERN_TMOD_HELPER_PY: string;
|
|
5
45
|
export declare const KERN_JS_HELPER_PY: string;
|
|
6
46
|
export declare const KERN_JS_ARRAY_HELPERS_PY: string;
|
|
7
47
|
export declare const KERN_JS_OBJECT_HELPERS_PY: string;
|
|
48
|
+
export declare const KERN_JS_NUMBER_HELPERS_PY: string;
|
|
49
|
+
export declare const KERN_JS_MATH_HELPERS_PY: string;
|
|
50
|
+
export declare const KERN_JS_ARRAY_FROM_HELPER_PY: string;
|
|
8
51
|
export declare const KERN_JS_STRING_HELPERS_PY: string;
|
|
52
|
+
export declare const KERN_REGEX_MATCH_HELPER_PY: string;
|
|
53
|
+
export declare const KERN_REGEX_MATCHALL_HELPER_PY: string;
|
|
@@ -1,3 +1,113 @@
|
|
|
1
|
+
// Slice S7 — dual-sentinel nullish/equality substrate. `_KERN_UNDEFINED` is a
|
|
2
|
+
// FIRST-CLASS Python value distinct from `None`: `undefined` is nullish with
|
|
3
|
+
// `null` (loose `==` crossing TRUE, `??`/`?.` treat both as nullish) but is NOT
|
|
4
|
+
// strictly equal to `null` (`===` FALSE). The two equality helpers split the JS
|
|
5
|
+
// `==` (loose) and `===` (strict) operators that previously both lowered to
|
|
6
|
+
// Python `==`:
|
|
7
|
+
// _kern_is_nullish(x) -> x is None or x is _KERN_UNDEFINED
|
|
8
|
+
// _kern_strict_equal(a, b) -> SameValue-ish: a nullish operand is equal only
|
|
9
|
+
// to the SAME nullish identity (undefined===undefined,
|
|
10
|
+
// null===null, but undefined!==null); otherwise `==`.
|
|
11
|
+
// _kern_loose_equal(a, b) -> both-nullish crossing is TRUE; else strict.
|
|
12
|
+
// The sentinel is matched by IDENTITY (`is`), never by value, so the undefined
|
|
13
|
+
// `__bool__ = False` override never leaks into the equality semantics. The block
|
|
14
|
+
// self-defines `_KERN_UNDEFINED` via the same idempotent `try/except NameError`
|
|
15
|
+
// guard the fmt/array/js helper blocks use, so it stands alone if registered
|
|
16
|
+
// without them.
|
|
17
|
+
export const KERN_NULLISH_HELPER_PY = [
|
|
18
|
+
'try:',
|
|
19
|
+
' _KERN_UNDEFINED',
|
|
20
|
+
'except NameError:',
|
|
21
|
+
' class _KernUndefined:',
|
|
22
|
+
' def __bool__(self): return False',
|
|
23
|
+
" def __repr__(self): return 'undefined'",
|
|
24
|
+
" def __str__(self): return 'undefined'",
|
|
25
|
+
' _KERN_UNDEFINED = _KernUndefined()',
|
|
26
|
+
'',
|
|
27
|
+
'def _kern_is_nullish(x):',
|
|
28
|
+
' return x is None or x is _KERN_UNDEFINED',
|
|
29
|
+
'',
|
|
30
|
+
'def _kern_strict_equal(a, b):',
|
|
31
|
+
' if a is _KERN_UNDEFINED or b is _KERN_UNDEFINED:',
|
|
32
|
+
' return a is b',
|
|
33
|
+
' if a is None or b is None:',
|
|
34
|
+
' return a is b',
|
|
35
|
+
// Python `bool` subclasses `int`, so `0 == False` / `1 == True` are True — but
|
|
36
|
+
// JS `0 === false` / `1 === true` are FALSE. Reject a bool-vs-non-bool pair
|
|
37
|
+
// before the value compare so the numeric/boolean type distinction survives.
|
|
38
|
+
' if (type(a) is bool) != (type(b) is bool):',
|
|
39
|
+
' return False',
|
|
40
|
+
// Containers must recurse ELEMENT-WISE through `_kern_strict_equal`, not Python
|
|
41
|
+
// `==`: Python list/dict `==` compares elements with `==`, which re-leaks the
|
|
42
|
+
// `0 == False` / `1 == True` bool⊂int conflation one level down (`[0] ==
|
|
43
|
+
// [False]` is True). The KERN core runtime compares structurally with
|
|
44
|
+
// kind-discrimination at EVERY level, so the Python target must too or
|
|
45
|
+
// `[0] === [false]` diverges (True on Python vs False in core). Lists/tuples
|
|
46
|
+
// (array-kind) compare by length + positional recursion; dicts (record-kind)
|
|
47
|
+
// by key set + per-key recursion. Strings stay on `==` (no element kinds).
|
|
48
|
+
' if isinstance(a, (list, tuple)) and isinstance(b, (list, tuple)):',
|
|
49
|
+
' if len(a) != len(b):',
|
|
50
|
+
' return False',
|
|
51
|
+
' return all(_kern_strict_equal(__k_x, __k_y) for __k_x, __k_y in zip(a, b))',
|
|
52
|
+
' if isinstance(a, dict) and isinstance(b, dict):',
|
|
53
|
+
' if a.keys() != b.keys():',
|
|
54
|
+
' return False',
|
|
55
|
+
' return all(_kern_strict_equal(a[__k_k], b[__k_k]) for __k_k in a)',
|
|
56
|
+
// A list-vs-dict (or container-vs-scalar) mismatch is unequal — Python `==`
|
|
57
|
+
// already returns False there, but make it explicit so the recursion above is
|
|
58
|
+
// the ONLY container path and a scalar `==` never sees a container pair.
|
|
59
|
+
' if isinstance(a, (list, tuple, dict)) or isinstance(b, (list, tuple, dict)):',
|
|
60
|
+
' return False',
|
|
61
|
+
' return a == b',
|
|
62
|
+
'',
|
|
63
|
+
'def _kern_loose_equal(a, b):',
|
|
64
|
+
' if _kern_is_nullish(a) and _kern_is_nullish(b):',
|
|
65
|
+
' return True',
|
|
66
|
+
' return _kern_strict_equal(a, b)',
|
|
67
|
+
].join('\n');
|
|
68
|
+
// Slice S7 — sentinel-aware `Json.stringify` / `JSON.stringify` shim. Raw
|
|
69
|
+
// `json.dumps` cannot model JS `JSON.stringify`'s undefined handling, so the
|
|
70
|
+
// Python target routes through `_kern_json_stringify`:
|
|
71
|
+
// - top-level `_KERN_UNDEFINED` → returns the sentinel itself (host-observed
|
|
72
|
+
// `undefined`), matching JS `JSON.stringify(undefined) === undefined`.
|
|
73
|
+
// - object property whose value is the sentinel → key OMITTED.
|
|
74
|
+
// - array element that is the sentinel → JSON `null`.
|
|
75
|
+
// - rules apply recursively into nested objects/arrays.
|
|
76
|
+
// - `None` stays JSON `null`; compact separators + ensure_ascii=False
|
|
77
|
+
// preserve the existing byte-for-byte parity for sentinel-free inputs.
|
|
78
|
+
// `_kern_json_prepare` returns a sentinel-free structure that `json.dumps` can
|
|
79
|
+
// serialize; the top-level sentinel is short-circuited before dumps runs. The
|
|
80
|
+
// block self-defines `_KERN_UNDEFINED` via the idempotent guard so it stands
|
|
81
|
+
// alone. It references `__k_json`, which the emitter supplies via the stdlib
|
|
82
|
+
// table's `requires.py: 'json'` → `import json as __k_json` (kept, NOT
|
|
83
|
+
// self-imported here, so a body that ALSO calls `Json.parse` shares one import).
|
|
84
|
+
export const KERN_JSON_STRINGIFY_SHIM_PY = [
|
|
85
|
+
'try:',
|
|
86
|
+
' _KERN_UNDEFINED',
|
|
87
|
+
'except NameError:',
|
|
88
|
+
' class _KernUndefined:',
|
|
89
|
+
' def __bool__(self): return False',
|
|
90
|
+
" def __repr__(self): return 'undefined'",
|
|
91
|
+
" def __str__(self): return 'undefined'",
|
|
92
|
+
' _KERN_UNDEFINED = _KernUndefined()',
|
|
93
|
+
'',
|
|
94
|
+
'def _kern_json_prepare(__k_v):',
|
|
95
|
+
' if isinstance(__k_v, dict):',
|
|
96
|
+
' __k_out = {}',
|
|
97
|
+
' for __k_k, __k_val in __k_v.items():',
|
|
98
|
+
' if __k_val is _KERN_UNDEFINED:',
|
|
99
|
+
' continue',
|
|
100
|
+
' __k_out[__k_k] = _kern_json_prepare(__k_val)',
|
|
101
|
+
' return __k_out',
|
|
102
|
+
' if isinstance(__k_v, (list, tuple)):',
|
|
103
|
+
' return [None if __k_e is _KERN_UNDEFINED else _kern_json_prepare(__k_e) for __k_e in __k_v]',
|
|
104
|
+
' return __k_v',
|
|
105
|
+
'',
|
|
106
|
+
'def _kern_json_stringify(__k_v):',
|
|
107
|
+
' if __k_v is _KERN_UNDEFINED:',
|
|
108
|
+
' return _KERN_UNDEFINED',
|
|
109
|
+
' return __k_json.dumps(_kern_json_prepare(__k_v), separators=(",", ":"), ensure_ascii=False)',
|
|
110
|
+
].join('\n');
|
|
1
111
|
export const KERN_PAIR_HELPERS_PY = [
|
|
2
112
|
'def _kern_pairs(__k_v):',
|
|
3
113
|
' return __k_v.items() if hasattr(__k_v, "items") else iter(__k_v)',
|
|
@@ -92,6 +202,126 @@ export const KERN_I32_HELPER_PY = [
|
|
|
92
202
|
' return 0',
|
|
93
203
|
' return ((val & 0xFFFFFFFF) ^ 0x80000000) - 0x80000000',
|
|
94
204
|
].join('\n');
|
|
205
|
+
/**
|
|
206
|
+
* ToNumericPrimitive substrate (slice 0.75) — Python twin of the
|
|
207
|
+
* `@kernlang/core` `to-numeric` decision kernel.
|
|
208
|
+
*
|
|
209
|
+
* Single Python-side source of numeric coercion truth for the FROZEN primitive
|
|
210
|
+
* domain (numbers, ECMA numeric strings, booleans, null, undefined sentinel).
|
|
211
|
+
* The emitted Python encodes the ECMA-262 StringNumericLiteral grammar
|
|
212
|
+
* EXPLICITLY rather than delegating to `float(...)`, because `float()` diverges
|
|
213
|
+
* from JS `Number()` on three load-bearing cases: it accepts numeric separators
|
|
214
|
+
* (`float('1_000') == 1000.0`), case-insensitive infinity/NaN words
|
|
215
|
+
* (`float('infinity')`, `float('nan')`), and raises on `0x`/`0b`/`0o` prefixes.
|
|
216
|
+
*
|
|
217
|
+
* Return-type contract (tribunal amendments 1 & 2):
|
|
218
|
+
* - `_kern_to_number(x)` -> Python `float` for EVERY numeric output
|
|
219
|
+
* (bool/null/hex/binary/octal inputs included); `-0.0` sign survives; NaN
|
|
220
|
+
* and ±inf preserved.
|
|
221
|
+
* - `_kern_string_to_number(text)` -> `float` (NaN for any non-grammar string).
|
|
222
|
+
* - `_kern_number_to_int32(n)` -> `int` (signed 32-bit, shift-mask domain).
|
|
223
|
+
* - `_kern_number_to_uint32(n)` -> `int` (unsigned 32-bit).
|
|
224
|
+
* - `_kern_to_int32(x)` -> `int` = int32(to_number(x)).
|
|
225
|
+
* - `_kern_to_uint32(x)` -> `int` = uint32(to_number(x)).
|
|
226
|
+
* - `_kern_to_integer_or_infinity(x)` -> `float` (int-valued, or ±inf).
|
|
227
|
+
*
|
|
228
|
+
* Fail-closed: objects/arrays/functions/symbols/custom-valueOf RAISE
|
|
229
|
+
* `_KernNumericCoercionError` (the caller decides) — full ToPrimitive deferred.
|
|
230
|
+
*
|
|
231
|
+
* Single-source-of-int32 note: this block is the coercion-correct int32 path
|
|
232
|
+
* going forward. The legacy `_i32` (KERN_I32_HELPER_PY) embeds its OWN
|
|
233
|
+
* float()-based coercion and is still wired to production
|
|
234
|
+
* (codegen-body-python.ts ToInt32 lowering). Slice 0.75 is a PURE ADDITION —
|
|
235
|
+
* nothing is rerouted here, so both coexist; the future routing slice retires
|
|
236
|
+
* `_i32` in favor of `_kern_to_int32` once the fallback count hits zero.
|
|
237
|
+
*
|
|
238
|
+
* The ECMA whitespace string below is the exact `StrWhiteSpace` set
|
|
239
|
+
* (`WhiteSpace` + `LineTerminator`) JS trims from a StringNumericLiteral, kept
|
|
240
|
+
* byte-aligned with `ECMA_STR_WHITESPACE` in the TS kernel.
|
|
241
|
+
*/
|
|
242
|
+
export const KERN_TO_NUMBER_HELPER_PY = [
|
|
243
|
+
'import math',
|
|
244
|
+
'import re',
|
|
245
|
+
'',
|
|
246
|
+
'try:',
|
|
247
|
+
' _KERN_UNDEFINED',
|
|
248
|
+
'except NameError:',
|
|
249
|
+
' class _KernUndefined:',
|
|
250
|
+
' def __bool__(self): return False',
|
|
251
|
+
" def __repr__(self): return 'undefined'",
|
|
252
|
+
" def __str__(self): return 'undefined'",
|
|
253
|
+
' _KERN_UNDEFINED = _KernUndefined()',
|
|
254
|
+
'',
|
|
255
|
+
'class _KernNumericCoercionError(TypeError):',
|
|
256
|
+
' pass',
|
|
257
|
+
'',
|
|
258
|
+
// ECMA StrWhiteSpace = WhiteSpace + LineTerminator. Encoded as \u escapes so
|
|
259
|
+
// the emitted source stays ASCII; Python materializes the real code points.
|
|
260
|
+
"_KERN_ECMA_WS = '\\t\\n\\x0b\\x0c\\r \\xa0\\u1680\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u2028\\u2029\\u202f\\u205f\\u3000\\ufeff'",
|
|
261
|
+
"_KERN_DECIMAL_RE = re.compile(r'^[+-]?(?:(?:[0-9]+(?:\\.[0-9]*)?|\\.[0-9]+)(?:[eE][+-]?[0-9]+)?|[0-9]+)$')",
|
|
262
|
+
"_KERN_HEX_RE = re.compile(r'^0[xX][0-9a-fA-F]+$')",
|
|
263
|
+
"_KERN_BIN_RE = re.compile(r'^0[bB][01]+$')",
|
|
264
|
+
"_KERN_OCT_RE = re.compile(r'^0[oO][0-7]+$')",
|
|
265
|
+
'',
|
|
266
|
+
'def _kern_string_to_number(text):',
|
|
267
|
+
' s = text.strip(_KERN_ECMA_WS)',
|
|
268
|
+
" if s == '':",
|
|
269
|
+
' return 0.0',
|
|
270
|
+
" if s == 'Infinity' or s == '+Infinity':",
|
|
271
|
+
" return float('inf')",
|
|
272
|
+
" if s == '-Infinity':",
|
|
273
|
+
" return float('-inf')",
|
|
274
|
+
' if _KERN_HEX_RE.match(s):',
|
|
275
|
+
' return float(int(s[2:], 16))',
|
|
276
|
+
' if _KERN_BIN_RE.match(s):',
|
|
277
|
+
' return float(int(s[2:], 2))',
|
|
278
|
+
' if _KERN_OCT_RE.match(s):',
|
|
279
|
+
' return float(int(s[2:], 8))',
|
|
280
|
+
' if not _KERN_DECIMAL_RE.match(s):',
|
|
281
|
+
" return float('nan')",
|
|
282
|
+
' try:',
|
|
283
|
+
' return float(s)',
|
|
284
|
+
' except ValueError:',
|
|
285
|
+
" return float('nan')",
|
|
286
|
+
'',
|
|
287
|
+
'def _kern_to_number(x):',
|
|
288
|
+
' if x is _KERN_UNDEFINED:',
|
|
289
|
+
" return float('nan')",
|
|
290
|
+
' if x is None:',
|
|
291
|
+
' return 0.0',
|
|
292
|
+
' if isinstance(x, bool):',
|
|
293
|
+
' return 1.0 if x else 0.0',
|
|
294
|
+
' if isinstance(x, (int, float)):',
|
|
295
|
+
' return float(x)',
|
|
296
|
+
' if isinstance(x, str):',
|
|
297
|
+
' return _kern_string_to_number(x)',
|
|
298
|
+
" raise _KernNumericCoercionError('KERN ToNumber supports only primitive values (slice-0.75); full ToPrimitive deferred')",
|
|
299
|
+
'',
|
|
300
|
+
'def _kern_number_to_int32(n):',
|
|
301
|
+
" if n != n or n == 0 or n in (float('inf'), float('-inf')):",
|
|
302
|
+
' return 0',
|
|
303
|
+
' i = math.trunc(n)',
|
|
304
|
+
' return ((i & 0xFFFFFFFF) ^ 0x80000000) - 0x80000000',
|
|
305
|
+
'',
|
|
306
|
+
'def _kern_number_to_uint32(n):',
|
|
307
|
+
" if n != n or n == 0 or n in (float('inf'), float('-inf')):",
|
|
308
|
+
' return 0',
|
|
309
|
+
' return math.trunc(n) & 0xFFFFFFFF',
|
|
310
|
+
'',
|
|
311
|
+
'def _kern_to_int32(x):',
|
|
312
|
+
' return _kern_number_to_int32(_kern_to_number(x))',
|
|
313
|
+
'',
|
|
314
|
+
'def _kern_to_uint32(x):',
|
|
315
|
+
' return _kern_number_to_uint32(_kern_to_number(x))',
|
|
316
|
+
'',
|
|
317
|
+
'def _kern_to_integer_or_infinity(x):',
|
|
318
|
+
' n = _kern_to_number(x)',
|
|
319
|
+
' if n != n:',
|
|
320
|
+
' return 0.0',
|
|
321
|
+
" if n in (float('inf'), float('-inf')):",
|
|
322
|
+
' return n',
|
|
323
|
+
' return float(math.trunc(n))',
|
|
324
|
+
].join('\n');
|
|
95
325
|
export const KERN_TMOD_HELPER_PY = [
|
|
96
326
|
'import math',
|
|
97
327
|
'def _tmod(a, b):',
|
|
@@ -109,11 +339,36 @@ export const KERN_TMOD_HELPER_PY = [
|
|
|
109
339
|
' return fa - math.trunc(fa / fb) * fb',
|
|
110
340
|
].join('\n');
|
|
111
341
|
export const KERN_JS_HELPER_PY = [
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
342
|
+
// Slice S4 — ToBoolean / KERN truthiness substrate. `js_truthy` is an EXPLICIT
|
|
343
|
+
// falsy-set predicate over the KERN value domain: falsy iff x is the undefined
|
|
344
|
+
// sentinel, None, a boolean False, numeric zero (+0/-0.0/0j), NaN, or "".
|
|
345
|
+
// Everything else is truthy — INCLUDING [], {}, callables, class instances, and
|
|
346
|
+
// user objects whose Python __bool__/__len__ are falsy. It NEVER delegates to
|
|
347
|
+
// bool(x), len(x), x.__bool__(), x.__len__(), or a ToNumber string conversion:
|
|
348
|
+
// "0", "false", " " are all truthy; only "" is falsy. `bool` is checked BEFORE
|
|
349
|
+
// the numeric branch because Python `bool` subclasses `int`. The undefined
|
|
350
|
+
// sentinel is matched by IDENTITY (`is _KERN_UNDEFINED`) — its __bool__ = False
|
|
351
|
+
// override is for bare-truthiness positions only and must NOT be generalized to
|
|
352
|
+
// user objects. `_kern_truthy` is the canonical name; `js_truthy` is the alias
|
|
353
|
+
// the existing array-predicate lowerings (filter/some/every/find-family) call.
|
|
354
|
+
'try:',
|
|
355
|
+
' _KERN_UNDEFINED',
|
|
356
|
+
'except NameError:',
|
|
357
|
+
' class _KernUndefined:',
|
|
358
|
+
' def __bool__(self): return False',
|
|
359
|
+
" def __repr__(self): return 'undefined'",
|
|
360
|
+
" def __str__(self): return 'undefined'",
|
|
361
|
+
' _KERN_UNDEFINED = _KernUndefined()',
|
|
362
|
+
'def _kern_truthy(x):',
|
|
363
|
+
' if x is _KERN_UNDEFINED or x is None or x is False: return False',
|
|
364
|
+
' if isinstance(x, bool): return x',
|
|
365
|
+
// `x == x` is False only for NaN, so `x != 0 and x == x` rejects both numeric
|
|
366
|
+
// zero (incl. -0.0 and 0j) and NaN without delegating to math.isnan / bool().
|
|
367
|
+
' if isinstance(x, (int, float, complex)): return x != 0 and x == x',
|
|
368
|
+
' if isinstance(x, str): return len(x) > 0',
|
|
116
369
|
' return True',
|
|
370
|
+
'def js_truthy(x):',
|
|
371
|
+
' return _kern_truthy(x)',
|
|
117
372
|
'def js_equals(a, b):',
|
|
118
373
|
' return a == b',
|
|
119
374
|
].join('\n');
|
|
@@ -169,6 +424,19 @@ export const KERN_JS_ARRAY_HELPERS_PY = [
|
|
|
169
424
|
' return __k_list',
|
|
170
425
|
].join('\n');
|
|
171
426
|
export const KERN_JS_OBJECT_HELPERS_PY = [
|
|
427
|
+
// Slice S7 — `Object.keys/values/entries` must throw TypeError parity for BOTH
|
|
428
|
+
// null and the undefined sentinel (JS `Object.keys(undefined)` throws). The
|
|
429
|
+
// sentinel is defined here via the idempotent guard so the identity check is
|
|
430
|
+
// safe even when this block is registered without the fmt/nullish blocks.
|
|
431
|
+
'try:',
|
|
432
|
+
' _KERN_UNDEFINED',
|
|
433
|
+
'except NameError:',
|
|
434
|
+
' class _KernUndefined:',
|
|
435
|
+
' def __bool__(self): return False',
|
|
436
|
+
" def __repr__(self): return 'undefined'",
|
|
437
|
+
" def __str__(self): return 'undefined'",
|
|
438
|
+
' _KERN_UNDEFINED = _KernUndefined()',
|
|
439
|
+
'',
|
|
172
440
|
'def _kern_js_is_array_index(__k_key):',
|
|
173
441
|
' __k_s = str(__k_key)',
|
|
174
442
|
' if not __k_s.isdigit(): return False',
|
|
@@ -177,7 +445,7 @@ export const KERN_JS_OBJECT_HELPERS_PY = [
|
|
|
177
445
|
' return 0 <= __k_n < 4294967295 and __k_s == str(__k_n)',
|
|
178
446
|
'',
|
|
179
447
|
'def _kern_js_property_items(__k_obj):',
|
|
180
|
-
' if __k_obj is None:',
|
|
448
|
+
' if __k_obj is None or __k_obj is _KERN_UNDEFINED:',
|
|
181
449
|
' raise TypeError("Cannot convert undefined or null to object")',
|
|
182
450
|
' if hasattr(__k_obj, "items"):',
|
|
183
451
|
' __k_raw = list(__k_obj.items())',
|
|
@@ -204,6 +472,196 @@ export const KERN_JS_OBJECT_HELPERS_PY = [
|
|
|
204
472
|
'',
|
|
205
473
|
'def _kern_js_object_entries(__k_obj):',
|
|
206
474
|
' return [[__k_k, __k_v] for __k_k, __k_v in _kern_js_property_items(__k_obj)]',
|
|
475
|
+
'',
|
|
476
|
+
'def _kern_js_object_assign(__k_target, *__k_sources):',
|
|
477
|
+
' if __k_target is None or __k_target is _KERN_UNDEFINED:',
|
|
478
|
+
' raise TypeError("Cannot convert undefined or null to object")',
|
|
479
|
+
' if hasattr(__k_target, "update"):',
|
|
480
|
+
' __k_out = __k_target',
|
|
481
|
+
' elif isinstance(__k_target, list):',
|
|
482
|
+
' __k_out = __k_target',
|
|
483
|
+
' elif isinstance(__k_target, str):',
|
|
484
|
+
' __k_out = {str(__k_i): __k_ch for __k_i, __k_ch in enumerate(__k_target)}',
|
|
485
|
+
' else:',
|
|
486
|
+
' __k_out = {}',
|
|
487
|
+
' for __k_src in __k_sources:',
|
|
488
|
+
' if __k_src is None or __k_src is _KERN_UNDEFINED:',
|
|
489
|
+
' continue',
|
|
490
|
+
' for __k_k, __k_v in _kern_js_property_items(__k_src):',
|
|
491
|
+
' if isinstance(__k_out, list):',
|
|
492
|
+
' if not _kern_js_is_array_index(__k_k):',
|
|
493
|
+
' raise TypeError("Object.assign cannot attach non-index properties to Python list target")',
|
|
494
|
+
' __k_i = int(__k_k)',
|
|
495
|
+
' while len(__k_out) <= __k_i:',
|
|
496
|
+
' __k_out.append(_KERN_UNDEFINED)',
|
|
497
|
+
' __k_out[__k_i] = __k_v',
|
|
498
|
+
' else:',
|
|
499
|
+
' __k_out[__k_k] = __k_v',
|
|
500
|
+
' return __k_out',
|
|
501
|
+
].join('\n');
|
|
502
|
+
// `Number.isInteger` / `Number.isSafeInteger` do NO coercion — a non-number
|
|
503
|
+
// argument (incl. a boolean) is ALWAYS false (`Number.isInteger("5")` → false,
|
|
504
|
+
// `Number.isInteger(true)` → false). Python's `bool` subclasses `int`, so the
|
|
505
|
+
// integer test MUST reject `bool` explicitly or `Number.isInteger(true)` wrongly
|
|
506
|
+
// returns True. NaN/±Infinity are non-integers. `isSafeInteger` additionally
|
|
507
|
+
// requires `abs(x) <= 2**53 - 1`.
|
|
508
|
+
export const KERN_JS_NUMBER_HELPERS_PY = [
|
|
509
|
+
'def _kern_number_is_integer(__k_x):',
|
|
510
|
+
' if isinstance(__k_x, bool):',
|
|
511
|
+
' return False',
|
|
512
|
+
' if isinstance(__k_x, int):',
|
|
513
|
+
' return True',
|
|
514
|
+
' if isinstance(__k_x, float):',
|
|
515
|
+
' return __k_x == __k_x and __k_x not in (float("inf"), float("-inf")) and __k_x.is_integer()',
|
|
516
|
+
' return False',
|
|
517
|
+
'',
|
|
518
|
+
'def _kern_number_is_safe_integer(__k_x):',
|
|
519
|
+
' if not _kern_number_is_integer(__k_x):',
|
|
520
|
+
' return False',
|
|
521
|
+
' return abs(__k_x) <= 9007199254740991',
|
|
522
|
+
].join('\n');
|
|
523
|
+
export const KERN_JS_MATH_HELPERS_PY = [
|
|
524
|
+
'import math',
|
|
525
|
+
'',
|
|
526
|
+
'def _kern_math_is_negative_zero(__k_n):',
|
|
527
|
+
' return __k_n == 0 and math.copysign(1.0, __k_n) < 0',
|
|
528
|
+
'',
|
|
529
|
+
'def _kern_math_nan():',
|
|
530
|
+
' return float("nan")',
|
|
531
|
+
'',
|
|
532
|
+
'def _kern_math_round(__k_x):',
|
|
533
|
+
' __k_n = _kern_to_number(__k_x)',
|
|
534
|
+
' if __k_n != __k_n or __k_n in (float("inf"), float("-inf")) or __k_n == 0:',
|
|
535
|
+
' return __k_n',
|
|
536
|
+
' __k_floor = math.floor(__k_n)',
|
|
537
|
+
' __k_r = __k_floor + (1 if __k_n - __k_floor >= 0.5 else 0)',
|
|
538
|
+
' if __k_r == 0 and __k_n < 0:',
|
|
539
|
+
' return -0.0',
|
|
540
|
+
' return __k_r',
|
|
541
|
+
'',
|
|
542
|
+
'def _kern_math_floor(__k_x):',
|
|
543
|
+
' __k_n = _kern_to_number(__k_x)',
|
|
544
|
+
' if __k_n != __k_n or __k_n in (float("inf"), float("-inf")) or __k_n == 0:',
|
|
545
|
+
' return __k_n',
|
|
546
|
+
' return math.floor(__k_n)',
|
|
547
|
+
'',
|
|
548
|
+
'def _kern_math_trunc(__k_x):',
|
|
549
|
+
' __k_n = _kern_to_number(__k_x)',
|
|
550
|
+
' if __k_n != __k_n or __k_n in (float("inf"), float("-inf")) or __k_n == 0:',
|
|
551
|
+
' return __k_n',
|
|
552
|
+
' __k_r = math.trunc(__k_n)',
|
|
553
|
+
' if __k_r == 0 and __k_n < 0:',
|
|
554
|
+
' return -0.0',
|
|
555
|
+
' return __k_r',
|
|
556
|
+
'',
|
|
557
|
+
'def _kern_math_sign(__k_x):',
|
|
558
|
+
' __k_n = _kern_to_number(__k_x)',
|
|
559
|
+
' if __k_n != __k_n or __k_n == 0:',
|
|
560
|
+
' return __k_n',
|
|
561
|
+
' return 1 if __k_n > 0 else -1',
|
|
562
|
+
'',
|
|
563
|
+
'def _kern_math_max(*__k_args):',
|
|
564
|
+
' if len(__k_args) == 0:',
|
|
565
|
+
' return float("-inf")',
|
|
566
|
+
' __k_best = float("-inf")',
|
|
567
|
+
' for __k_arg in __k_args:',
|
|
568
|
+
' __k_n = _kern_to_number(__k_arg)',
|
|
569
|
+
' if __k_n != __k_n:',
|
|
570
|
+
' return _kern_math_nan()',
|
|
571
|
+
' if __k_n > __k_best or (__k_n == 0 and __k_best == 0 and not _kern_math_is_negative_zero(__k_n) and _kern_math_is_negative_zero(__k_best)):',
|
|
572
|
+
' __k_best = __k_n',
|
|
573
|
+
' return __k_best',
|
|
574
|
+
'',
|
|
575
|
+
'def _kern_math_min(*__k_args):',
|
|
576
|
+
' if len(__k_args) == 0:',
|
|
577
|
+
' return float("inf")',
|
|
578
|
+
' __k_best = float("inf")',
|
|
579
|
+
' for __k_arg in __k_args:',
|
|
580
|
+
' __k_n = _kern_to_number(__k_arg)',
|
|
581
|
+
' if __k_n != __k_n:',
|
|
582
|
+
' return _kern_math_nan()',
|
|
583
|
+
' if __k_n < __k_best or (__k_n == 0 and __k_best == 0 and _kern_math_is_negative_zero(__k_n) and not _kern_math_is_negative_zero(__k_best)):',
|
|
584
|
+
' __k_best = __k_n',
|
|
585
|
+
' return __k_best',
|
|
586
|
+
].join('\n');
|
|
587
|
+
export const KERN_JS_ARRAY_FROM_HELPER_PY = [
|
|
588
|
+
'import inspect',
|
|
589
|
+
'',
|
|
590
|
+
'try:',
|
|
591
|
+
' RangeError',
|
|
592
|
+
'except NameError:',
|
|
593
|
+
' class RangeError(ValueError):',
|
|
594
|
+
' pass',
|
|
595
|
+
'',
|
|
596
|
+
'try:',
|
|
597
|
+
' _KERN_UNDEFINED',
|
|
598
|
+
'except NameError:',
|
|
599
|
+
' class _KernUndefined:',
|
|
600
|
+
' def __bool__(self): return False',
|
|
601
|
+
" def __repr__(self): return 'undefined'",
|
|
602
|
+
" def __str__(self): return 'undefined'",
|
|
603
|
+
' _KERN_UNDEFINED = _KernUndefined()',
|
|
604
|
+
'',
|
|
605
|
+
'def _kern_array_like_get(__k_source, __k_index):',
|
|
606
|
+
' if isinstance(__k_source, dict):',
|
|
607
|
+
' if __k_index in __k_source:',
|
|
608
|
+
' return __k_source[__k_index]',
|
|
609
|
+
' __k_key = str(__k_index)',
|
|
610
|
+
' return __k_source[__k_key] if __k_key in __k_source else _KERN_UNDEFINED',
|
|
611
|
+
' try:',
|
|
612
|
+
' return __k_source[__k_index]',
|
|
613
|
+
' except Exception:',
|
|
614
|
+
' return _KERN_UNDEFINED',
|
|
615
|
+
'',
|
|
616
|
+
'def _kern_array_like_length(__k_source):',
|
|
617
|
+
' if isinstance(__k_source, dict):',
|
|
618
|
+
' __k_len = __k_source.get("length", 0)',
|
|
619
|
+
' else:',
|
|
620
|
+
' __k_len = getattr(__k_source, "length", None)',
|
|
621
|
+
' if __k_len is None:',
|
|
622
|
+
' return None',
|
|
623
|
+
' try:',
|
|
624
|
+
' __k_num = _kern_to_number(__k_len)',
|
|
625
|
+
' except Exception:',
|
|
626
|
+
' return 0',
|
|
627
|
+
' if __k_num != __k_num or __k_num <= 0:',
|
|
628
|
+
' return 0',
|
|
629
|
+
' if __k_num == float("inf"):',
|
|
630
|
+
' raise RangeError("Invalid array length")',
|
|
631
|
+
' __k_length = int(__k_num)',
|
|
632
|
+
' if __k_length > 4294967295:',
|
|
633
|
+
' raise RangeError("Invalid array length")',
|
|
634
|
+
' return __k_length',
|
|
635
|
+
'',
|
|
636
|
+
'def _kern_array_from(__k_source, __k_mapper=None):',
|
|
637
|
+
' if __k_source is None or __k_source is _KERN_UNDEFINED:',
|
|
638
|
+
' raise TypeError("Array.from requires an array-like or iterable source")',
|
|
639
|
+
' __k_len = _kern_array_like_length(__k_source)',
|
|
640
|
+
' if __k_len is not None:',
|
|
641
|
+
' __k_values = [_kern_array_like_get(__k_source, __k_i) for __k_i in range(__k_len)]',
|
|
642
|
+
' elif isinstance(__k_source, str):',
|
|
643
|
+
' __k_values = list(__k_source)',
|
|
644
|
+
' else:',
|
|
645
|
+
' try:',
|
|
646
|
+
' __k_values = list(__k_source)',
|
|
647
|
+
' except TypeError:',
|
|
648
|
+
' __k_values = []',
|
|
649
|
+
' if __k_mapper is None:',
|
|
650
|
+
' return list(__k_values)',
|
|
651
|
+
' return [_kern_array_from_map(__k_mapper, __k_value, __k_index) for __k_index, __k_value in enumerate(__k_values)]',
|
|
652
|
+
'',
|
|
653
|
+
'def _kern_array_from_map(__k_mapper, __k_value, __k_index):',
|
|
654
|
+
' try:',
|
|
655
|
+
' __k_sig = inspect.signature(__k_mapper)',
|
|
656
|
+
' __k_params = list(__k_sig.parameters.values())',
|
|
657
|
+
' if any(__k_p.kind == inspect.Parameter.VAR_POSITIONAL for __k_p in __k_params):',
|
|
658
|
+
' return __k_mapper(__k_value, __k_index)',
|
|
659
|
+
' __k_positional = [__k_p for __k_p in __k_params if __k_p.kind in (inspect.Parameter.POSITIONAL_ONLY, inspect.Parameter.POSITIONAL_OR_KEYWORD)]',
|
|
660
|
+
' if len(__k_positional) < 2:',
|
|
661
|
+
' return __k_mapper(__k_value)',
|
|
662
|
+
' except (TypeError, ValueError):',
|
|
663
|
+
' pass',
|
|
664
|
+
' return __k_mapper(__k_value, __k_index)',
|
|
207
665
|
].join('\n');
|
|
208
666
|
export const KERN_JS_STRING_HELPERS_PY = [
|
|
209
667
|
'def _kern_js_split_limit(__k_limit):',
|
|
@@ -260,4 +718,50 @@ export const KERN_JS_STRING_HELPERS_PY = [
|
|
|
260
718
|
' __k_pos = __k_end',
|
|
261
719
|
' return "".join(__k_parts)',
|
|
262
720
|
].join('\n');
|
|
721
|
+
// Milestone C, Slice 3 — portable regex MATCH-SET result helpers. JS `RegExp`
|
|
722
|
+
// methods and Python `re` differ in RESULT SHAPE (not just pattern); these two
|
|
723
|
+
// helpers normalize the Python `re.Match` surface into the canonical
|
|
724
|
+
// target-neutral KERN shapes the TS emitter produces natively from a
|
|
725
|
+
// `RegExpMatchArray`. They are byte-for-byte the lowering the Slice-3 oracle
|
|
726
|
+
// (`.agon-goals/regex-slice3/oracle/run_py.py::canon_match_obj` / `lower_matchAll`)
|
|
727
|
+
// certifies against node.
|
|
728
|
+
//
|
|
729
|
+
// _kern_regex_match(pat, s, flags) -> `.match(s)` WITHOUT /g
|
|
730
|
+
// JS `String.match` (non-global) returns a match-array carrying `.index` and
|
|
731
|
+
// `.groups`, or `null` on no match. Python `re.search` returns a `re.Match`
|
|
732
|
+
// OBJECT (a DIFFERENT surface: `m[0]` raises, no `.index` attr) or `None`.
|
|
733
|
+
// We converge both onto `{full, groups, index, named}` | None — so a
|
|
734
|
+
// downstream `m["full"]` / `m["index"]` / `m["named"]` reads identically on
|
|
735
|
+
// each target. (THE load-bearing portability fix — spec §4.1, killer row
|
|
736
|
+
// `match_no_g_groups_KILLER`.)
|
|
737
|
+
//
|
|
738
|
+
// _kern_regex_matchall(pat, s, flags) -> `.matchAll(s)` (/g required)
|
|
739
|
+
// JS `[...s.matchAll(re)]` yields match objects {full, g1.., index}; Python
|
|
740
|
+
// `re.finditer` yields `re.Match`es. We shape both to
|
|
741
|
+
// `[{full, groups, index}, ...]`, INCLUDING zero-width advances (e.g. `/x*/g`
|
|
742
|
+
// over "abc" -> 4 empty matches at 0..3) which `re.finditer` already enumerates
|
|
743
|
+
// identically to JS on modern engines (killer row `matchAll_empty_KILLER`).
|
|
744
|
+
export const KERN_REGEX_MATCH_HELPER_PY = [
|
|
745
|
+
'def _kern_regex_match(__k_pat, __k_s, __k_flags):',
|
|
746
|
+
' __k_m = __k_re.search(__k_pat, __k_s, __k_flags)',
|
|
747
|
+
' if __k_m is None:',
|
|
748
|
+
' return None',
|
|
749
|
+
' return {',
|
|
750
|
+
' "full": __k_m.group(0),',
|
|
751
|
+
' "groups": [__k_g for __k_g in __k_m.groups()],',
|
|
752
|
+
' "index": __k_m.start(),',
|
|
753
|
+
' "named": dict(__k_m.groupdict()),',
|
|
754
|
+
' }',
|
|
755
|
+
].join('\n');
|
|
756
|
+
export const KERN_REGEX_MATCHALL_HELPER_PY = [
|
|
757
|
+
'def _kern_regex_matchall(__k_pat, __k_s, __k_flags):',
|
|
758
|
+
' return [',
|
|
759
|
+
' {',
|
|
760
|
+
' "full": __k_m.group(0),',
|
|
761
|
+
' "groups": [__k_g for __k_g in __k_m.groups()],',
|
|
762
|
+
' "index": __k_m.start(),',
|
|
763
|
+
' }',
|
|
764
|
+
' for __k_m in __k_re.finditer(__k_pat, __k_s, __k_flags)',
|
|
765
|
+
' ]',
|
|
766
|
+
].join('\n');
|
|
263
767
|
//# sourceMappingURL=helpers.js.map
|