@kernlang/python 3.5.6-canary.199.1.c927d232 → 3.5.6-canary.202.1.31706b95

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.
Files changed (61) hide show
  1. package/dist/adapters/django.d.ts +49 -0
  2. package/dist/adapters/django.js +151 -0
  3. package/dist/adapters/django.js.map +1 -0
  4. package/dist/adapters/fastapi.d.ts +43 -0
  5. package/dist/adapters/fastapi.js +139 -0
  6. package/dist/adapters/fastapi.js.map +1 -0
  7. package/dist/codegen-body-python.d.ts +51 -2
  8. package/dist/codegen-body-python.js +281 -26
  9. package/dist/codegen-body-python.js.map +1 -1
  10. package/dist/codegen-python.d.ts +1 -0
  11. package/dist/codegen-python.js +1 -1
  12. package/dist/codegen-python.js.map +1 -1
  13. package/dist/core/emit-imports.d.ts +11 -0
  14. package/dist/core/emit-imports.js +166 -0
  15. package/dist/core/emit-imports.js.map +1 -0
  16. package/dist/core/emit-models.d.ts +14 -0
  17. package/dist/core/emit-models.js +86 -0
  18. package/dist/core/emit-models.js.map +1 -0
  19. package/dist/core/expr/helpers.d.ts +5 -0
  20. package/dist/core/expr/helpers.js +62 -0
  21. package/dist/core/expr/helpers.js.map +1 -0
  22. package/dist/core/expr/index.d.ts +9 -0
  23. package/dist/core/expr/index.js +2046 -0
  24. package/dist/core/expr/index.js.map +1 -0
  25. package/dist/core/fence-diagnostics.d.ts +17 -0
  26. package/dist/core/fence-diagnostics.js +86 -0
  27. package/dist/core/fence-diagnostics.js.map +1 -0
  28. package/dist/core/handlers/index.d.ts +74 -0
  29. package/dist/core/handlers/index.js +462 -0
  30. package/dist/core/handlers/index.js.map +1 -0
  31. package/dist/core/index.d.ts +3 -0
  32. package/dist/core/index.js +4 -0
  33. package/dist/core/index.js.map +1 -0
  34. package/dist/core/type-mapper.d.ts +4 -0
  35. package/dist/core/type-mapper.js +8 -0
  36. package/dist/core/type-mapper.js.map +1 -0
  37. package/dist/fastapi-portable.d.ts +7 -1
  38. package/dist/fastapi-portable.js +97 -74
  39. package/dist/fastapi-portable.js.map +1 -1
  40. package/dist/fastapi-response.d.ts +0 -4
  41. package/dist/fastapi-response.js +1 -1868
  42. package/dist/fastapi-response.js.map +1 -1
  43. package/dist/fastapi-route.js +24 -3
  44. package/dist/fastapi-route.js.map +1 -1
  45. package/dist/fastapi-utils.d.ts +2 -1
  46. package/dist/fastapi-utils.js +2 -58
  47. package/dist/fastapi-utils.js.map +1 -1
  48. package/dist/generators/data.d.ts +3 -1
  49. package/dist/generators/data.js +28 -1
  50. package/dist/generators/data.js.map +1 -1
  51. package/dist/index.d.ts +4 -3
  52. package/dist/index.js +4 -3
  53. package/dist/index.js.map +1 -1
  54. package/dist/ir-semantics/python-leg.js +5 -0
  55. package/dist/ir-semantics/python-leg.js.map +1 -1
  56. package/dist/targets/python.d.ts +32 -0
  57. package/dist/targets/python.js +176 -0
  58. package/dist/targets/python.js.map +1 -0
  59. package/dist/transpiler-fastapi.js +23 -66
  60. package/dist/transpiler-fastapi.js.map +1 -1
  61. package/package.json +2 -2
@@ -39,6 +39,7 @@
39
39
  * relative indent on the `return __k_tN` line; the wrapper prepends the
40
40
  * surrounding indent so the post-emit result nests correctly. */
41
41
  import { applyTemplate, isPostfixMutationOperator, isSupportedAssignOperator, KERN_STDLIB_MODULES, lookupStdlib, needsArgParens, needsBinaryParens, parseExpression, suggestStdlibMethod, } from '@kernlang/core';
42
+ import { KERN_FMT_HELPER_PY, KERN_I32_HELPER_PY, KERN_PAIR_HELPERS_PY, KERN_TMOD_HELPER_PY, } from './core/expr/index.js';
42
43
  const INDENT_STEP = ' ';
43
44
  function freshCtx(options) {
44
45
  return {
@@ -49,6 +50,7 @@ function freshCtx(options) {
49
50
  shadowedSymbols: new Set(),
50
51
  localScopes: [],
51
52
  regexScopes: [],
53
+ renameStack: [],
52
54
  propagateStyle: options?.propagateStyle ?? 'value',
53
55
  usedPropagation: false,
54
56
  tryDepth: 0,
@@ -74,18 +76,6 @@ function freshCtx(options) {
74
76
  * async iterable; sync data is wrapped at iteration entry).
75
77
  *
76
78
  * Both helpers are pure functions on the input; no captures, no globals. */
77
- export const KERN_PAIR_HELPERS_PY = [
78
- 'def _kern_pairs(__k_v):',
79
- ' return __k_v.items() if hasattr(__k_v, "items") else iter(__k_v)',
80
- '',
81
- 'async def _kern_async_pairs(__k_v):',
82
- ' if hasattr(__k_v, "__aiter__"):',
83
- ' async for __k_item in __k_v:',
84
- ' yield __k_item',
85
- ' else:',
86
- ' for __k_item in _kern_pairs(__k_v):',
87
- ' yield __k_item',
88
- ].join('\n');
89
79
  /** KERN-canonical interpolation formatter for `fmt` / template literals.
90
80
  * Python `f"{v}"` uses `str()`, which gives `True`/`False`/`None` — diverging
91
81
  * from KERN's canonical lowercase `true`/`false`/`null` that TS template
@@ -97,14 +87,6 @@ export const KERN_PAIR_HELPERS_PY = [
97
87
  * `True` → `"True"`. Co-located with the codegen so the production emitter and
98
88
  * the differential harness use byte-identical defs; emitted at module scope
99
89
  * via `BodyEmitResult.helpers` whenever an interpolation is wrapped. */
100
- export const KERN_FMT_HELPER_PY = [
101
- 'def _kern_fmt(__k_v):',
102
- ' if isinstance(__k_v, bool):',
103
- " return 'true' if __k_v else 'false'",
104
- ' if __k_v is None:',
105
- " return 'null'",
106
- ' return str(__k_v)',
107
- ].join('\n');
108
90
  /** Emit the body of a native KERN handler as Python source. Returns the
109
91
  * joined body text. Each top-level line is unindented; nested `if`-bodies
110
92
  * carry one level of 4-space indent per level of nesting.
@@ -148,8 +130,31 @@ export function emitNativeKernBodyPython(handlerNode, options) {
148
130
  * when `propagateStyle: 'http-exception'` is in effect. */
149
131
  export function emitNativeKernBodyPythonWithImports(handlerNode, options) {
150
132
  const ctx = freshCtx(options);
151
- const code = emitChildrenPy(handlerNode.children ?? [], ctx, '').join('\n');
152
- return { code, imports: ctx.imports, usedPropagation: ctx.usedPropagation, helpers: ctx.helpers };
133
+ // Push the param/outer-binding scope ABOVE the function-body scope so an
134
+ // inner-block `let x` that shadows a param is detected by
135
+ // `maybeRenameOnShadow` (nero red-team Challenge 2). `emitChildrenPy`
136
+ // pushes its own scope on top; we pop ours after it returns.
137
+ const outerBindings = options?.outerBindings ?? [];
138
+ if (outerBindings.length > 0) {
139
+ ctx.localScopes.push(new Map(outerBindings.map((n) => [n, 'const'])));
140
+ // `null` is the existing "no active regex binding" sentinel — consumed
141
+ // by `lookupRegexBinding` (returns null when the scope has the name but
142
+ // no regex literal was assigned to it). Mirroring it here keeps regex
143
+ // and local-binding scope stacks index-aligned.
144
+ ctx.regexScopes.push(new Map(outerBindings.map((n) => [n, null])));
145
+ ctx.renameStack.push(new Map());
146
+ }
147
+ try {
148
+ const code = emitChildrenPy(handlerNode.children ?? [], ctx, '').join('\n');
149
+ return { code, imports: ctx.imports, usedPropagation: ctx.usedPropagation, helpers: ctx.helpers };
150
+ }
151
+ finally {
152
+ if (outerBindings.length > 0) {
153
+ ctx.localScopes.pop();
154
+ ctx.regexScopes.pop();
155
+ ctx.renameStack.pop();
156
+ }
157
+ }
153
158
  }
154
159
  /** Body-statement node types that map to a SINGLE emitted line and may carry
155
160
  * an inline same-line trailing comment captured by the migrator into a
@@ -169,6 +174,7 @@ function emitChildrenPy(children, ctx, indent, initialBindings = []) {
169
174
  const lines = [];
170
175
  ctx.localScopes.push(new Map(initialBindings));
171
176
  ctx.regexScopes.push(new Map(initialBindings.map(([name]) => [name, null])));
177
+ ctx.renameStack.push(new Map());
172
178
  try {
173
179
  for (let i = 0; i < children.length; i++) {
174
180
  const child = children[i];
@@ -530,9 +536,43 @@ function emitChildrenPy(children, ctx, indent, initialBindings = []) {
530
536
  finally {
531
537
  ctx.localScopes.pop();
532
538
  ctx.regexScopes.pop();
539
+ ctx.renameStack.pop();
533
540
  }
534
541
  return lines;
535
542
  }
543
+ /** Returns the rename for `name` from the innermost scope that has one, else
544
+ * `name` itself. Consulted in ident emission and at `let`/`assign` LHS
545
+ * rendering so a shadowed inner `let x` (emitted as `__k_shadow_x_N`) and
546
+ * its references inside the block resolve consistently, while outer
547
+ * references after the block still see the user-facing name. */
548
+ function resolveLocalRename(ctx, name) {
549
+ for (let i = ctx.renameStack.length - 1; i >= 0; i--) {
550
+ const scope = ctx.renameStack[i];
551
+ const renamed = scope.get(name);
552
+ if (renamed !== undefined)
553
+ return renamed;
554
+ }
555
+ return name;
556
+ }
557
+ /** Returns the renamed name if `let name=` here would shadow a binding in
558
+ * any OUTER scope; otherwise returns `name` unchanged. Used by `emitLetPy`
559
+ * to give an inner-block shadow a unique Python name + record the rename
560
+ * in the current scope so within-block references resolve to it. Returns
561
+ * `name` for function-body lets (no outer scope to shadow) and for
562
+ * non-shadowing inner lets (so unrelated locals stay user-friendly). */
563
+ function maybeRenameOnShadow(ctx, name) {
564
+ // Only the inner-most CURRENT scope is the "newcomer"; check OUTER scopes.
565
+ if (ctx.localScopes.length < 2)
566
+ return name;
567
+ for (let i = ctx.localScopes.length - 2; i >= 0; i--) {
568
+ if (ctx.localScopes[i].has(name)) {
569
+ const renamed = `__k_shadow_${name}_${++ctx.gensymCounter}`;
570
+ ctx.renameStack.at(-1)?.set(name, renamed);
571
+ return renamed;
572
+ }
573
+ }
574
+ return name;
575
+ }
536
576
  function emitRangeForPy(node, ctx, indent) {
537
577
  const props = (node.props ?? {});
538
578
  const name = String(props.name ?? '');
@@ -797,15 +837,22 @@ function emitSetPy(node, ctx) {
797
837
  }
798
838
  function emitLetPy(node, ctx) {
799
839
  const props = (node.props ?? {});
800
- const name = String(props.name ?? '_');
840
+ const userName = String(props.name ?? '_');
801
841
  validateBodyLetKind(props.kind);
802
- declareLocalBinding(ctx, name, props.kind === 'let' ? 'let' : 'const');
842
+ declareLocalBinding(ctx, userName, props.kind === 'let' ? 'let' : 'const');
843
+ // Block-scope fix: an inner `let` that shadows an outer binding gets a
844
+ // gensym'd Python name so TS `let x=1; if(c){let x=2}; return x` (returns 1)
845
+ // doesn't degrade to Python's flat scoping (would return 2). The rename is
846
+ // stored in the current scope's renameStack and resolved by every ident
847
+ // emission inside this block; outer references after the block see the
848
+ // user-facing name (no entry in any in-scope rename map).
849
+ const name = maybeRenameOnShadow(ctx, userName);
803
850
  const rawValue = props.value;
804
851
  if (rawValue === undefined || rawValue === '') {
805
852
  return [`${name} = None`];
806
853
  }
807
854
  const valueIR = parseExpression(String(rawValue));
808
- setRegexBinding(ctx, name, valueIR.kind === 'regexLit' ? valueIR : null);
855
+ setRegexBinding(ctx, userName, valueIR.kind === 'regexLit' ? valueIR : null);
809
856
  if (valueIR.kind === 'propagate' && valueIR.op === '?') {
810
857
  rejectPropagationInsideTry(ctx);
811
858
  const tmp = `__k_t${++ctx.gensymCounter}`;
@@ -1250,13 +1297,23 @@ function emitPyExprCtx(node, ctx) {
1250
1297
  case 'regexLit':
1251
1298
  ctx.imports.add('re');
1252
1299
  return `__k_re.compile(${pyRegexPattern(node)}, ${pyRegexFlags(node.flags, { allowGlobal: true })})`;
1253
- case 'ident':
1300
+ case 'ident': {
1301
+ // Block-scope rename takes precedence — an inner `let x` that shadows
1302
+ // an outer binding was emitted with a gensym (`__k_shadow_x_N`) and
1303
+ // every in-block reference must use the same gensym. Walk renameStack
1304
+ // top-to-bottom (most-inner scope wins); after the inner block ends
1305
+ // its scope is popped, so post-block references naturally see the
1306
+ // outer user-facing name again.
1307
+ const blockRename = resolveLocalRename(ctx, node.name);
1308
+ if (blockRename !== node.name)
1309
+ return blockRename;
1254
1310
  // Slice 3a — apply symbol-map rename so KERN-form `userId` becomes
1255
1311
  // Python-form `user_id`. Identifiers not in the map (locals, globals,
1256
1312
  // module names) pass through unchanged.
1257
1313
  if (ctx.shadowedSymbols.has(node.name))
1258
1314
  return node.name;
1259
1315
  return ctx.symbolMap[node.name] ?? node.name;
1316
+ }
1260
1317
  case 'member':
1261
1318
  case 'call':
1262
1319
  case 'index': {
@@ -1302,6 +1359,16 @@ function emitPyExprCtx(node, ctx) {
1302
1359
  return out;
1303
1360
  }
1304
1361
  case 'binary': {
1362
+ if (node.op === '|' ||
1363
+ node.op === '&' ||
1364
+ node.op === '^' ||
1365
+ node.op === '<<' ||
1366
+ node.op === '>>' ||
1367
+ node.op === '%') {
1368
+ const transformed = lowerBitwiseAndModuloAST(node);
1369
+ registerHelpers(transformed, ctx);
1370
+ return emitPyExprCtx(transformed, ctx);
1371
+ }
1305
1372
  // Slice 2c — arithmetic / comparison / logical lowering for Python.
1306
1373
  // Use precedence-aware paren-wrapping so `a + b * c` doesn't redundantly
1307
1374
  // wrap the right side (`a + (b * c)`) — same rule as the TS side.
@@ -1359,6 +1426,11 @@ function emitPyExprCtx(node, ctx) {
1359
1426
  return `${lp} ${op} ${rp}`;
1360
1427
  }
1361
1428
  case 'unary': {
1429
+ if (node.op === '~') {
1430
+ const transformed = lowerBitwiseAndModuloAST(node);
1431
+ registerHelpers(transformed, ctx);
1432
+ return emitPyExprCtx(transformed, ctx);
1433
+ }
1362
1434
  // Slice 2c — `!x` → `not x`, `-x` → `-x`.
1363
1435
  // Slice typeof — expose the now-eligible native KERN `typeof` shape on
1364
1436
  // Python too. Dynamic Python values are an approximation of JS typeof:
@@ -1828,4 +1900,187 @@ function lowerListLambdaPython(moduleName, methodName, call, ctx) {
1828
1900
  ctx.shadowedSymbols = previous;
1829
1901
  }
1830
1902
  }
1903
+ export function lowerBitwiseAndModuloAST(node) {
1904
+ switch (node.kind) {
1905
+ case 'binary': {
1906
+ const left = lowerBitwiseAndModuloAST(node.left);
1907
+ const right = lowerBitwiseAndModuloAST(node.right);
1908
+ if (node.op === '|' || node.op === '&' || node.op === '^' || node.op === '<<' || node.op === '>>') {
1909
+ let rewrittenRight = right;
1910
+ if (node.op === '<<' || node.op === '>>') {
1911
+ const i32Right = wrapInI32(right);
1912
+ rewrittenRight = {
1913
+ kind: 'binary',
1914
+ op: '&',
1915
+ left: i32Right,
1916
+ right: { kind: 'numLit', value: 31, raw: '31' },
1917
+ };
1918
+ }
1919
+ else {
1920
+ rewrittenRight = wrapInI32(right);
1921
+ }
1922
+ const i32Left = wrapInI32(left);
1923
+ const bitwiseNode = {
1924
+ kind: 'binary',
1925
+ op: node.op,
1926
+ left: i32Left,
1927
+ right: rewrittenRight,
1928
+ };
1929
+ return wrapInI32(bitwiseNode);
1930
+ }
1931
+ else if (node.op === '%') {
1932
+ return {
1933
+ kind: 'call',
1934
+ callee: { kind: 'ident', name: '_tmod' },
1935
+ args: [left, right],
1936
+ optional: false,
1937
+ };
1938
+ }
1939
+ return { ...node, left, right };
1940
+ }
1941
+ case 'unary': {
1942
+ const argument = lowerBitwiseAndModuloAST(node.argument);
1943
+ if (node.op === '~') {
1944
+ const i32Arg = wrapInI32(argument);
1945
+ const unaryNode = {
1946
+ kind: 'unary',
1947
+ op: '~',
1948
+ argument: i32Arg,
1949
+ };
1950
+ return wrapInI32(unaryNode);
1951
+ }
1952
+ return { ...node, argument };
1953
+ }
1954
+ case 'tmplLit':
1955
+ return { ...node, expressions: node.expressions.map(lowerBitwiseAndModuloAST) };
1956
+ case 'member':
1957
+ return { ...node, object: lowerBitwiseAndModuloAST(node.object) };
1958
+ case 'index':
1959
+ return { ...node, object: lowerBitwiseAndModuloAST(node.object), index: lowerBitwiseAndModuloAST(node.index) };
1960
+ case 'call':
1961
+ return {
1962
+ ...node,
1963
+ callee: lowerBitwiseAndModuloAST(node.callee),
1964
+ args: node.args.map(lowerBitwiseAndModuloAST),
1965
+ };
1966
+ case 'lambda':
1967
+ return { ...node, body: lowerBitwiseAndModuloAST(node.body) };
1968
+ case 'spread':
1969
+ return { ...node, argument: lowerBitwiseAndModuloAST(node.argument) };
1970
+ case 'await':
1971
+ return { ...node, argument: lowerBitwiseAndModuloAST(node.argument) };
1972
+ case 'new':
1973
+ return { ...node, argument: lowerBitwiseAndModuloAST(node.argument) };
1974
+ case 'typeAssert':
1975
+ return { ...node, expression: lowerBitwiseAndModuloAST(node.expression) };
1976
+ case 'nonNull':
1977
+ return { ...node, expression: lowerBitwiseAndModuloAST(node.expression) };
1978
+ case 'propagate':
1979
+ return { ...node, argument: lowerBitwiseAndModuloAST(node.argument) };
1980
+ case 'objectLit':
1981
+ return {
1982
+ ...node,
1983
+ entries: node.entries.map((e) => 'kind' in e && e.kind === 'spread'
1984
+ ? { kind: 'spread', argument: lowerBitwiseAndModuloAST(e.argument) }
1985
+ : { ...e, value: lowerBitwiseAndModuloAST(e.value) }),
1986
+ };
1987
+ case 'arrayLit':
1988
+ return { ...node, items: node.items.map(lowerBitwiseAndModuloAST) };
1989
+ case 'conditional':
1990
+ return {
1991
+ ...node,
1992
+ test: lowerBitwiseAndModuloAST(node.test),
1993
+ consequent: lowerBitwiseAndModuloAST(node.consequent),
1994
+ alternate: lowerBitwiseAndModuloAST(node.alternate),
1995
+ };
1996
+ default:
1997
+ return node;
1998
+ }
1999
+ }
2000
+ function wrapInI32(node) {
2001
+ return {
2002
+ kind: 'call',
2003
+ callee: { kind: 'ident', name: '_i32' },
2004
+ args: [node],
2005
+ optional: false,
2006
+ };
2007
+ }
2008
+ export function registerHelpers(node, ctx) {
2009
+ switch (node.kind) {
2010
+ case 'call':
2011
+ if (node.callee.kind === 'ident') {
2012
+ if (node.callee.name === '_i32') {
2013
+ ctx.helpers.add(KERN_I32_HELPER_PY);
2014
+ }
2015
+ else if (node.callee.name === '_tmod') {
2016
+ ctx.helpers.add(KERN_TMOD_HELPER_PY);
2017
+ }
2018
+ }
2019
+ registerHelpers(node.callee, ctx);
2020
+ for (const arg of node.args) {
2021
+ registerHelpers(arg, ctx);
2022
+ }
2023
+ break;
2024
+ case 'binary':
2025
+ registerHelpers(node.left, ctx);
2026
+ registerHelpers(node.right, ctx);
2027
+ break;
2028
+ case 'unary':
2029
+ registerHelpers(node.argument, ctx);
2030
+ break;
2031
+ case 'tmplLit':
2032
+ for (const expr of node.expressions) {
2033
+ registerHelpers(expr, ctx);
2034
+ }
2035
+ break;
2036
+ case 'member':
2037
+ registerHelpers(node.object, ctx);
2038
+ break;
2039
+ case 'index':
2040
+ registerHelpers(node.object, ctx);
2041
+ registerHelpers(node.index, ctx);
2042
+ break;
2043
+ case 'lambda':
2044
+ registerHelpers(node.body, ctx);
2045
+ break;
2046
+ case 'spread':
2047
+ registerHelpers(node.argument, ctx);
2048
+ break;
2049
+ case 'await':
2050
+ registerHelpers(node.argument, ctx);
2051
+ break;
2052
+ case 'new':
2053
+ registerHelpers(node.argument, ctx);
2054
+ break;
2055
+ case 'typeAssert':
2056
+ registerHelpers(node.expression, ctx);
2057
+ break;
2058
+ case 'nonNull':
2059
+ registerHelpers(node.expression, ctx);
2060
+ break;
2061
+ case 'propagate':
2062
+ registerHelpers(node.argument, ctx);
2063
+ break;
2064
+ case 'objectLit':
2065
+ for (const e of node.entries) {
2066
+ if ('kind' in e && e.kind === 'spread') {
2067
+ registerHelpers(e.argument, ctx);
2068
+ }
2069
+ else {
2070
+ registerHelpers(e.value, ctx);
2071
+ }
2072
+ }
2073
+ break;
2074
+ case 'arrayLit':
2075
+ for (const item of node.items) {
2076
+ registerHelpers(item, ctx);
2077
+ }
2078
+ break;
2079
+ case 'conditional':
2080
+ registerHelpers(node.test, ctx);
2081
+ registerHelpers(node.consequent, ctx);
2082
+ registerHelpers(node.alternate, ctx);
2083
+ break;
2084
+ }
2085
+ }
1831
2086
  //# sourceMappingURL=codegen-body-python.js.map