@kernlang/python 3.5.8-canary.209.1.8afcd7e7 → 3.5.8-canary.212.1.1d9726e6
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.js +142 -1
- package/dist/codegen-body-python.js.map +1 -1
- package/dist/codegen-python.d.ts +1 -1
- package/dist/codegen-python.js +24 -2
- package/dist/codegen-python.js.map +1 -1
- package/dist/core/handlers/index.js +427 -2
- package/dist/core/handlers/index.js.map +1 -1
- package/dist/fastapi-portable.js +351 -3
- package/dist/fastapi-portable.js.map +1 -1
- package/dist/fastapi-route.js +36 -0
- package/dist/fastapi-route.js.map +1 -1
- package/dist/generators/ground.d.ts +11 -0
- package/dist/generators/ground.js +325 -9
- package/dist/generators/ground.js.map +1 -1
- package/dist/portable-collection-emitter.d.ts +3 -0
- package/dist/portable-collection-emitter.js +49 -0
- package/dist/portable-collection-emitter.js.map +1 -0
- package/dist/portable-object-emitter.d.ts +2 -0
- package/dist/portable-object-emitter.js +7 -0
- package/dist/portable-object-emitter.js.map +1 -0
- package/dist/portable-predicate-emitter.d.ts +1 -0
- package/dist/portable-predicate-emitter.js +109 -0
- package/dist/portable-predicate-emitter.js.map +1 -0
- package/dist/type-map.d.ts +1 -0
- package/dist/type-map.js +70 -0
- package/dist/type-map.js.map +1 -1
- package/package.json +2 -2
package/dist/codegen-python.js
CHANGED
|
@@ -9,7 +9,7 @@ import { generateConfig, generateConst, generateError, generateEvent, generateEx
|
|
|
9
9
|
// Data layer generators (model, repository, cache, dependency, service, union)
|
|
10
10
|
import { generatePythonCache, generatePythonDependency, generatePythonModel, generatePythonRepository, generatePythonService, generatePythonUnion, } from './generators/data.js';
|
|
11
11
|
// Ground layer generators (derive, transform, action, guard, assume, invariant, each, collect, branch, resolve, expect, recover)
|
|
12
|
-
import { generateAction, generateAssume, generateBranch, generateClamp, generateCollect, generateDerive, generateEach, generateExpect, generateGuard, generateInvariant, generateObjectMerge, generateRecover, generateResolve, generateTransform, setDispatcher, } from './generators/ground.js';
|
|
12
|
+
import { generateAction, generateAssume, generateBranch, generateClamp, generateCoalesce, generateCollect, generateCount, generateCountBy, generateDerive, generateEach, generateExpect, generateFirstDefined, generateFirstTruthy, generateGroupBy, generateGuard, generateIndexBy, generateInvariant, generateObjectMerge, generateObjectOmit, generateObjectPick, generatePartition, generateRecover, generateResolve, generateTransform, generateUniqueBy, setDispatcher, } from './generators/ground.js';
|
|
13
13
|
// Infra generators (job, storage, email)
|
|
14
14
|
import { generatePythonEmail, generatePythonJob, generatePythonStorage } from './generators/infra.js';
|
|
15
15
|
// Re-export helpers and annotation emitters for external consumers
|
|
@@ -17,7 +17,7 @@ export { emitPyLowConfidenceTodo, emitPyReasonAnnotations, firstChild, kids, p }
|
|
|
17
17
|
// Re-export individual generators so existing deep imports keep working
|
|
18
18
|
export { generateConfig, generateConst, generateError, generateEvent, generateFunction, generateImport, generateInterface, generateMachine, generateModule, generateStore, generateTest, generateType, generateUse, } from './generators/core.js';
|
|
19
19
|
export { formatPythonDefault, generatePythonCache, generatePythonDependency, generatePythonModel, generatePythonRepository, generatePythonService, generatePythonUnion, mapColumnToPython, } from './generators/data.js';
|
|
20
|
-
export { generateAction, generateAssume, generateBranch, generateClamp, generateCollect, generateDerive, generateEach, generateExpect, generateGuard, generateInvariant, generateObjectMerge, generateRecover, generateResolve, generateTransform, } from './generators/ground.js';
|
|
20
|
+
export { generateAction, generateAssume, generateBranch, generateClamp, generateCoalesce, generateCollect, generateCount, generateCountBy, generateDerive, generateEach, generateExpect, generateFirstDefined, generateFirstTruthy, generateGroupBy, generateGuard, generateIndexBy, generateInvariant, generateObjectMerge, generateObjectOmit, generateObjectPick, generatePartition, generateRecover, generateResolve, generateTransform, generateUniqueBy, } from './generators/ground.js';
|
|
21
21
|
export { generatePythonEmail, generatePythonJob, generatePythonStorage, } from './generators/infra.js';
|
|
22
22
|
/** Generate Python for any core language node. Returns string lines. */
|
|
23
23
|
export function generatePythonCoreNode(node, options = {}) {
|
|
@@ -77,8 +77,18 @@ export function generatePythonCoreNode(node, options = {}) {
|
|
|
77
77
|
return generateDerive(node);
|
|
78
78
|
case 'clamp':
|
|
79
79
|
return generateClamp(node);
|
|
80
|
+
case 'firstTruthy':
|
|
81
|
+
return generateFirstTruthy(node);
|
|
82
|
+
case 'coalesce':
|
|
83
|
+
return generateCoalesce(node);
|
|
84
|
+
case 'firstDefined':
|
|
85
|
+
return generateFirstDefined(node);
|
|
80
86
|
case 'objectMerge':
|
|
81
87
|
return generateObjectMerge(node);
|
|
88
|
+
case 'objectOmit':
|
|
89
|
+
return generateObjectOmit(node);
|
|
90
|
+
case 'objectPick':
|
|
91
|
+
return generateObjectPick(node);
|
|
82
92
|
case 'transform':
|
|
83
93
|
return generateTransform(node);
|
|
84
94
|
case 'action':
|
|
@@ -93,6 +103,18 @@ export function generatePythonCoreNode(node, options = {}) {
|
|
|
93
103
|
return generateEach(node);
|
|
94
104
|
case 'collect':
|
|
95
105
|
return generateCollect(node);
|
|
106
|
+
case 'count':
|
|
107
|
+
return generateCount(node);
|
|
108
|
+
case 'countBy':
|
|
109
|
+
return generateCountBy(node);
|
|
110
|
+
case 'uniqueBy':
|
|
111
|
+
return generateUniqueBy(node);
|
|
112
|
+
case 'groupBy':
|
|
113
|
+
return generateGroupBy(node);
|
|
114
|
+
case 'partition':
|
|
115
|
+
return generatePartition(node);
|
|
116
|
+
case 'indexBy':
|
|
117
|
+
return generateIndexBy(node);
|
|
96
118
|
case 'branch':
|
|
97
119
|
return generateBranch(node);
|
|
98
120
|
case 'resolve':
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codegen-python.js","sourceRoot":"","sources":["../src/codegen-python.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,mGAAmG;AACnG,OAAO,EACL,cAAc,EACd,aAAa,EACb,aAAa,EACb,aAAa,EACb,cAAc,EACd,gBAAgB,EAChB,cAAc,EACd,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,WAAW,GACZ,MAAM,sBAAsB,CAAC;AAC9B,+EAA+E;AAC/E,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACxB,mBAAmB,EACnB,wBAAwB,EACxB,qBAAqB,EACrB,mBAAmB,GACpB,MAAM,sBAAsB,CAAC;AAC9B,iIAAiI;AACjI,OAAO,EACL,cAAc,EACd,cAAc,EACd,cAAc,EACd,aAAa,EACb,eAAe,EACf,cAAc,EACd,YAAY,EACZ,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,mBAAmB,EACnB,eAAe,EACf,eAAe,EACf,iBAAiB,EACjB,aAAa,GACd,MAAM,wBAAwB,CAAC;AAEhC,yCAAyC;AACzC,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAEtG,mEAAmE;AACnE,OAAO,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,sBAAsB,CAAC;AAE7G,wEAAwE;AACxE,OAAO,EACL,cAAc,EACd,aAAa,EACb,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,cAAc,EACd,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,WAAW,GACZ,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,wBAAwB,EACxB,mBAAmB,EACnB,wBAAwB,EACxB,qBAAqB,EACrB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,cAAc,EACd,cAAc,EACd,cAAc,EACd,aAAa,EACb,eAAe,EACf,cAAc,EACd,YAAY,EACZ,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,mBAAmB,EACnB,eAAe,EACf,eAAe,EACf,iBAAiB,
|
|
1
|
+
{"version":3,"file":"codegen-python.js","sourceRoot":"","sources":["../src/codegen-python.ts"],"names":[],"mappings":"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"}
|
|
@@ -43,10 +43,14 @@
|
|
|
43
43
|
* needing to rewrite `body.x` → `body["x"]`. The wrapping is internal —
|
|
44
44
|
* adapters and external callers always see a plain `dict`.
|
|
45
45
|
*/
|
|
46
|
-
import { getChildren, getFirstChild, getProps } from '@kernlang/core';
|
|
46
|
+
import { emitStringKeyArray, getChildren, getFirstChild, getProps, parseKeys, parsePortableNonNegativeIntLiteral, parsePortablePathSegments, splitPortableExpressionList, } from '@kernlang/core';
|
|
47
47
|
import { isUnsupportedJsHandlerBody, unsupportedRawHandlerBody } from '../../fastapi-raw-handler.js';
|
|
48
48
|
import { derivePathParams, escapePyStr, indentHandler, slugify } from '../../fastapi-utils.js';
|
|
49
|
-
import {
|
|
49
|
+
import { emitPythonRoutePluckHelper, emitPythonRouteSortKeyHelper, pythonRouteCompactPredicate, } from '../../portable-collection-emitter.js';
|
|
50
|
+
import { pythonRouteRecordExpr, pythonRouteRecordPickExpr } from '../../portable-object-emitter.js';
|
|
51
|
+
import { emitPythonPredicateHelpers } from '../../portable-predicate-emitter.js';
|
|
52
|
+
import { mapTsTypeToPython, toPythonBindingName, toSnakeCase } from '../../type-map.js';
|
|
53
|
+
import { KERN_JS_OBJECT_HELPERS_PY } from '../expr/helpers.js';
|
|
50
54
|
import { rewriteExpr } from '../expr/index.js';
|
|
51
55
|
function indentHoistedDef(def, indent) {
|
|
52
56
|
return def.split('\n').map((line) => `${indent}${line}`);
|
|
@@ -84,6 +88,27 @@ function extractExprCode(val) {
|
|
|
84
88
|
function extractCodeOrString(val) {
|
|
85
89
|
return extractExprCode(val);
|
|
86
90
|
}
|
|
91
|
+
function pushJsObjectKeyCoercion(lines, indent, keyName) {
|
|
92
|
+
lines.push(`${indent}if ${keyName} is None:`);
|
|
93
|
+
lines.push(`${indent} ${keyName} = "null"`);
|
|
94
|
+
lines.push(`${indent}elif isinstance(${keyName}, bool):`);
|
|
95
|
+
lines.push(`${indent} ${keyName} = "true" if ${keyName} else "false"`);
|
|
96
|
+
lines.push(`${indent}elif isinstance(${keyName}, float):`);
|
|
97
|
+
lines.push(`${indent} if ${keyName} != ${keyName}:`);
|
|
98
|
+
lines.push(`${indent} ${keyName} = "NaN"`);
|
|
99
|
+
lines.push(`${indent} elif ${keyName} == float("inf"):`);
|
|
100
|
+
lines.push(`${indent} ${keyName} = "Infinity"`);
|
|
101
|
+
lines.push(`${indent} elif ${keyName} == float("-inf"):`);
|
|
102
|
+
lines.push(`${indent} ${keyName} = "-Infinity"`);
|
|
103
|
+
lines.push(`${indent} elif ${keyName}.is_integer():`);
|
|
104
|
+
lines.push(`${indent} ${keyName} = str(int(${keyName}))`);
|
|
105
|
+
lines.push(`${indent} else:`);
|
|
106
|
+
lines.push(`${indent} ${keyName} = str(${keyName})`);
|
|
107
|
+
lines.push(`${indent}elif not isinstance(${keyName}, (str, int)):`);
|
|
108
|
+
lines.push(`${indent} raise TypeError("keyed reshape selector must produce a scalar key")`);
|
|
109
|
+
lines.push(`${indent}else:`);
|
|
110
|
+
lines.push(`${indent} ${keyName} = str(${keyName})`);
|
|
111
|
+
}
|
|
87
112
|
function mapJsDefaultToPython(def) {
|
|
88
113
|
if (def === undefined || def === null)
|
|
89
114
|
return 'None';
|
|
@@ -301,6 +326,388 @@ function generatePurePythonStmt(child, indent, pathParams, bodyFields, authUser,
|
|
|
301
326
|
}
|
|
302
327
|
break;
|
|
303
328
|
}
|
|
329
|
+
case 'count': {
|
|
330
|
+
const name = toPythonBindingName(String(p.name || ''), 'count');
|
|
331
|
+
if (!name)
|
|
332
|
+
break;
|
|
333
|
+
const collection = rewriteExprPure(extractCodeOrString(p.in).trim(), indent);
|
|
334
|
+
lines.push(...collection.hoists);
|
|
335
|
+
const item = String(p.item || 'item');
|
|
336
|
+
const where = p.where ? extractCodeOrString(p.where) : undefined;
|
|
337
|
+
const predicateStr = p.predicate ? extractExprCode(p.predicate) || String(p.predicate) : undefined;
|
|
338
|
+
const typeAnnotation = p.type ? `: ${mapTsTypeToPython(String(p.type))}` : '';
|
|
339
|
+
if (predicateStr && where) {
|
|
340
|
+
throw new Error("count node cannot combine 'where' and 'predicate'");
|
|
341
|
+
}
|
|
342
|
+
if (predicateStr) {
|
|
343
|
+
const predicateExpr = rewriteExprPure(predicateStr, indent);
|
|
344
|
+
lines.push(...predicateExpr.hoists);
|
|
345
|
+
const absentVar = `__KernAbsent_${name}`;
|
|
346
|
+
const getPathVar = `__kern_get_path_${name}`;
|
|
347
|
+
const equalVar = `__kern_equal_${name}`;
|
|
348
|
+
const evalPredVar = `__kern_eval_predicate_${name}`;
|
|
349
|
+
const predicateValueVar = `__kern_predicate_${name}`;
|
|
350
|
+
lines.push(...emitPythonPredicateHelpers(indent, absentVar, getPathVar, equalVar, evalPredVar));
|
|
351
|
+
lines.push(`${indent}${predicateValueVar} = ${predicateExpr.expr}`);
|
|
352
|
+
lines.push(`${indent}${name}${typeAnnotation} = sum(1 for ${item} in ${collection.expr} if ${evalPredVar}(${predicateValueVar}, ${item}))`);
|
|
353
|
+
}
|
|
354
|
+
else if (where) {
|
|
355
|
+
const whereExpr = rewriteExprPure(where, indent);
|
|
356
|
+
lines.push(...whereExpr.hoists);
|
|
357
|
+
lines.push(`${indent}${name}${typeAnnotation} = sum(1 for ${item} in ${collection.expr} if ${whereExpr.expr})`);
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
lines.push(`${indent}${name}${typeAnnotation} = len(${collection.expr})`);
|
|
361
|
+
}
|
|
362
|
+
break;
|
|
363
|
+
}
|
|
364
|
+
case 'filter': {
|
|
365
|
+
const name = toPythonBindingName(String(p.name || ''), 'filter');
|
|
366
|
+
if (!name)
|
|
367
|
+
throw new Error("filter node requires a 'name' prop");
|
|
368
|
+
const inVal = extractCodeOrString(p.in).trim();
|
|
369
|
+
const collection = rewriteExprPure(inVal, indent);
|
|
370
|
+
lines.push(...collection.hoists);
|
|
371
|
+
const where = p.where ? extractExprCode(p.where) : undefined;
|
|
372
|
+
const predicateStr = p.predicate ? extractExprCode(p.predicate) || String(p.predicate) : undefined;
|
|
373
|
+
const item = String(p.item || 'item');
|
|
374
|
+
if (predicateStr && where) {
|
|
375
|
+
throw new Error("filter node cannot combine 'where' and 'predicate'");
|
|
376
|
+
}
|
|
377
|
+
if (predicateStr) {
|
|
378
|
+
const predicateExpr = rewriteExprPure(predicateStr, indent);
|
|
379
|
+
lines.push(...predicateExpr.hoists);
|
|
380
|
+
const absentVar = `__KernAbsent_${name}`;
|
|
381
|
+
const getPathVar = `__kern_get_path_${name}`;
|
|
382
|
+
const equalVar = `__kern_equal_${name}`;
|
|
383
|
+
const evalPredVar = `__kern_eval_predicate_${name}`;
|
|
384
|
+
const predicateValueVar = `__kern_predicate_${name}`;
|
|
385
|
+
lines.push(...emitPythonPredicateHelpers(indent, absentVar, getPathVar, equalVar, evalPredVar));
|
|
386
|
+
lines.push(`${indent}${predicateValueVar} = ${predicateExpr.expr}`);
|
|
387
|
+
lines.push(`${indent}${name} = [${item} for ${item} in ${collection.expr} if ${evalPredVar}(${predicateValueVar}, ${item})]`);
|
|
388
|
+
}
|
|
389
|
+
else if (where) {
|
|
390
|
+
const whereExpr = rewriteExprPure(where, indent);
|
|
391
|
+
lines.push(...whereExpr.hoists);
|
|
392
|
+
lines.push(`${indent}${name} = [${item} for ${item} in ${collection.expr} if ${whereExpr.expr}]`);
|
|
393
|
+
}
|
|
394
|
+
else {
|
|
395
|
+
throw new Error("filter node requires a 'where' or 'predicate' prop");
|
|
396
|
+
}
|
|
397
|
+
break;
|
|
398
|
+
}
|
|
399
|
+
case 'compact': {
|
|
400
|
+
const name = toPythonBindingName(String(p.name || ''), 'compact');
|
|
401
|
+
if (!name)
|
|
402
|
+
throw new Error("compact node requires a 'name' prop");
|
|
403
|
+
const inVal = extractCodeOrString(p.in).trim();
|
|
404
|
+
if (!inVal)
|
|
405
|
+
throw new Error("compact node requires an 'in' prop");
|
|
406
|
+
const item = 'item';
|
|
407
|
+
const collection = rewriteExprPure(inVal, indent);
|
|
408
|
+
lines.push(...collection.hoists);
|
|
409
|
+
const typeAnnotation = p.type ? `: ${mapTsTypeToPython(String(p.type))}` : '';
|
|
410
|
+
lines.push(`${indent}${name}${typeAnnotation} = [${item} for ${item} in ${collection.expr} if ${pythonRouteCompactPredicate(item)}]`);
|
|
411
|
+
break;
|
|
412
|
+
}
|
|
413
|
+
case 'pluck': {
|
|
414
|
+
const name = toPythonBindingName(String(p.name || ''), 'pluck');
|
|
415
|
+
if (!name)
|
|
416
|
+
throw new Error("pluck node requires a 'name' prop");
|
|
417
|
+
const inVal = extractCodeOrString(p.in).trim();
|
|
418
|
+
if (!inVal)
|
|
419
|
+
throw new Error("pluck node requires an 'in' prop");
|
|
420
|
+
const propVal = extractCodeOrString(p.prop).trim();
|
|
421
|
+
if (!propVal)
|
|
422
|
+
throw new Error("pluck node requires a 'prop' prop");
|
|
423
|
+
const collection = rewriteExprPure(inVal, indent);
|
|
424
|
+
lines.push(...collection.hoists);
|
|
425
|
+
const segments = parsePortablePathSegments(propVal, child, 'prop');
|
|
426
|
+
const pathExpr = emitStringKeyArray(segments);
|
|
427
|
+
const helperName = `__kern_pluck_${name}`;
|
|
428
|
+
const typeAnnotation = p.type ? `: ${mapTsTypeToPython(String(p.type))}` : '';
|
|
429
|
+
emitPythonRoutePluckHelper(lines, indent, helperName, pathExpr);
|
|
430
|
+
lines.push(`${indent}${name}${typeAnnotation} = [${helperName}(__kern_item) for __kern_item in ${collection.expr}]`);
|
|
431
|
+
break;
|
|
432
|
+
}
|
|
433
|
+
case 'take':
|
|
434
|
+
case 'drop': {
|
|
435
|
+
const kind = child.type;
|
|
436
|
+
const name = toPythonBindingName(String(p.name || ''), kind);
|
|
437
|
+
if (!name)
|
|
438
|
+
throw new Error(`${kind} node requires a 'name' prop`);
|
|
439
|
+
const inVal = extractCodeOrString(p.in).trim();
|
|
440
|
+
if (!inVal)
|
|
441
|
+
throw new Error(`${kind} node requires an 'in' prop`);
|
|
442
|
+
const nVal = extractCodeOrString(p.n).trim();
|
|
443
|
+
if (!nVal)
|
|
444
|
+
throw new Error(`${kind} node requires an 'n' prop`);
|
|
445
|
+
const collection = rewriteExprPure(inVal, indent);
|
|
446
|
+
lines.push(...collection.hoists);
|
|
447
|
+
const n = parsePortableNonNegativeIntLiteral(nVal, child, 'n');
|
|
448
|
+
const typeAnnotation = p.type ? `: ${mapTsTypeToPython(String(p.type))}` : '';
|
|
449
|
+
const slice = kind === 'take' ? `[:${n}]` : `[${n}:]`;
|
|
450
|
+
lines.push(`${indent}${name}${typeAnnotation} = ${collection.expr}${slice}`);
|
|
451
|
+
break;
|
|
452
|
+
}
|
|
453
|
+
case 'sort': {
|
|
454
|
+
const name = toPythonBindingName(String(p.name || ''), 'sort');
|
|
455
|
+
if (!name)
|
|
456
|
+
throw new Error("sort node requires a 'name' prop");
|
|
457
|
+
const inVal = extractCodeOrString(p.in).trim();
|
|
458
|
+
if (!inVal)
|
|
459
|
+
throw new Error("sort node requires an 'in' prop");
|
|
460
|
+
const collection = rewriteExprPure(inVal, indent);
|
|
461
|
+
lines.push(...collection.hoists);
|
|
462
|
+
const compareSource = extractCodeOrString(p.compare).trim();
|
|
463
|
+
const typeAnnotation = p.type ? `: ${mapTsTypeToPython(String(p.type))}` : '';
|
|
464
|
+
if (compareSource) {
|
|
465
|
+
const a = toPythonBindingName(String(p.a || 'a'), 'sort a');
|
|
466
|
+
const b = toPythonBindingName(String(p.b || 'b'), 'sort b');
|
|
467
|
+
if (a === b) {
|
|
468
|
+
throw new Error('portable route `sort` comparator operands must use distinct `a=` and `b=` names.');
|
|
469
|
+
}
|
|
470
|
+
const compare = rewriteExprPure(compareSource, indent);
|
|
471
|
+
if (compare.hoists.length > 0) {
|
|
472
|
+
throw new Error('portable route `sort compare=` cannot contain closures or expressions that need hoisted helpers.');
|
|
473
|
+
}
|
|
474
|
+
imports.add('import functools');
|
|
475
|
+
lines.push(`${indent}${name}${typeAnnotation} = sorted(${collection.expr}, key=functools.cmp_to_key(lambda ${a}, ${b}: ${compare.expr}))`);
|
|
476
|
+
}
|
|
477
|
+
else {
|
|
478
|
+
const helperName = `__kern_sort_key_${name}`;
|
|
479
|
+
emitPythonRouteSortKeyHelper(lines, indent, helperName);
|
|
480
|
+
lines.push(`${indent}${name}${typeAnnotation} = sorted(${collection.expr}, key=${helperName})`);
|
|
481
|
+
}
|
|
482
|
+
break;
|
|
483
|
+
}
|
|
484
|
+
case 'objectMerge': {
|
|
485
|
+
const name = toPythonBindingName(String(p.name || ''), 'objectMerge');
|
|
486
|
+
if (!name)
|
|
487
|
+
throw new Error("objectMerge node requires a 'name' prop");
|
|
488
|
+
const rawSources = extractCodeOrString(p.sources).trim();
|
|
489
|
+
if (!rawSources)
|
|
490
|
+
throw new Error("objectMerge node requires a 'sources' prop");
|
|
491
|
+
const sources = splitPortableExpressionList(rawSources, 'objectMerge sources=');
|
|
492
|
+
if (sources.length < 2)
|
|
493
|
+
throw new Error('portable route `objectMerge` requires at least two source expressions.');
|
|
494
|
+
const emitted = [];
|
|
495
|
+
for (const source of sources) {
|
|
496
|
+
if (source.startsWith('...')) {
|
|
497
|
+
throw new Error('portable route `objectMerge` sources must not start with `...`; spreading is implicit.');
|
|
498
|
+
}
|
|
499
|
+
const rewritten = rewriteExprPure(source, indent);
|
|
500
|
+
lines.push(...rewritten.hoists);
|
|
501
|
+
emitted.push(`**${pythonRouteRecordExpr(rewritten.expr)}`);
|
|
502
|
+
}
|
|
503
|
+
const typeAnnotation = p.type ? `: ${mapTsTypeToPython(String(p.type))}` : '';
|
|
504
|
+
lines.push(`${indent}${name}${typeAnnotation} = {${emitted.join(', ')}}`);
|
|
505
|
+
break;
|
|
506
|
+
}
|
|
507
|
+
case 'objectPick': {
|
|
508
|
+
const name = toPythonBindingName(String(p.name || ''), 'objectPick');
|
|
509
|
+
if (!name)
|
|
510
|
+
throw new Error("objectPick node requires a 'name' prop");
|
|
511
|
+
const inVal = extractCodeOrString(p.in).trim();
|
|
512
|
+
if (!inVal)
|
|
513
|
+
throw new Error("objectPick node requires an 'in' prop");
|
|
514
|
+
const rawKeys = extractCodeOrString(p.keys).trim();
|
|
515
|
+
if (!rawKeys)
|
|
516
|
+
throw new Error("objectPick node requires a 'keys' prop");
|
|
517
|
+
const source = rewriteExprPure(inVal, indent);
|
|
518
|
+
lines.push(...source.hoists);
|
|
519
|
+
const keys = emitStringKeyArray(parseKeys(rawKeys, child, 'objectPick keys='));
|
|
520
|
+
const typeAnnotation = p.type ? `: ${mapTsTypeToPython(String(p.type))}` : '';
|
|
521
|
+
lines.push(`${indent}${name}${typeAnnotation} = ${pythonRouteRecordPickExpr(source.expr, keys)}`);
|
|
522
|
+
break;
|
|
523
|
+
}
|
|
524
|
+
case 'objectOmit': {
|
|
525
|
+
const name = toPythonBindingName(String(p.name || ''), 'objectOmit');
|
|
526
|
+
if (!name)
|
|
527
|
+
throw new Error("objectOmit node requires a 'name' prop");
|
|
528
|
+
const inVal = extractCodeOrString(p.in).trim();
|
|
529
|
+
if (!inVal)
|
|
530
|
+
throw new Error("objectOmit node requires an 'in' prop");
|
|
531
|
+
const rawKeys = extractCodeOrString(p.keys).trim();
|
|
532
|
+
if (!rawKeys)
|
|
533
|
+
throw new Error("objectOmit node requires a 'keys' prop");
|
|
534
|
+
const source = rewriteExprPure(inVal, indent);
|
|
535
|
+
lines.push(...source.hoists);
|
|
536
|
+
const keys = emitStringKeyArray(parseKeys(rawKeys, child, 'objectOmit keys='));
|
|
537
|
+
const typeAnnotation = p.type ? `: ${mapTsTypeToPython(String(p.type))}` : '';
|
|
538
|
+
lines.push(`${indent}${name}${typeAnnotation} = {key: value for key, value in ${pythonRouteRecordExpr(source.expr)}.items() if key not in ${keys}}`);
|
|
539
|
+
break;
|
|
540
|
+
}
|
|
541
|
+
case 'objectKeys':
|
|
542
|
+
case 'objectValues':
|
|
543
|
+
case 'objectEntries': {
|
|
544
|
+
const nodeType = child.type;
|
|
545
|
+
const name = toPythonBindingName(String(p.name || ''), nodeType);
|
|
546
|
+
if (!name)
|
|
547
|
+
throw new Error(`${nodeType} node requires a 'name' prop`);
|
|
548
|
+
const inVal = extractCodeOrString(p.in).trim();
|
|
549
|
+
if (!inVal)
|
|
550
|
+
throw new Error(`${nodeType} node requires an 'in' prop`);
|
|
551
|
+
const source = rewriteExprPure(inVal, indent);
|
|
552
|
+
lines.push(...source.hoists);
|
|
553
|
+
imports.add(KERN_JS_OBJECT_HELPERS_PY);
|
|
554
|
+
const helper = nodeType === 'objectKeys'
|
|
555
|
+
? '_kern_js_object_keys'
|
|
556
|
+
: nodeType === 'objectValues'
|
|
557
|
+
? '_kern_js_object_values'
|
|
558
|
+
: '_kern_js_object_entries';
|
|
559
|
+
const typeAnnotation = p.type ? `: ${mapTsTypeToPython(String(p.type))}` : '';
|
|
560
|
+
lines.push(`${indent}${name}${typeAnnotation} = ${helper}(${pythonRouteRecordExpr(source.expr)})`);
|
|
561
|
+
break;
|
|
562
|
+
}
|
|
563
|
+
case 'uniqueBy': {
|
|
564
|
+
const name = toPythonBindingName(String(p.name || ''), 'uniqueBy');
|
|
565
|
+
if (!name)
|
|
566
|
+
throw new Error("uniqueBy node requires a 'name' prop");
|
|
567
|
+
const inVal = extractCodeOrString(p.in).trim();
|
|
568
|
+
if (!inVal)
|
|
569
|
+
throw new Error("uniqueBy node requires an 'in' prop");
|
|
570
|
+
const byVal = extractCodeOrString(p.by).trim();
|
|
571
|
+
if (!byVal)
|
|
572
|
+
throw new Error("uniqueBy node requires a 'by' prop");
|
|
573
|
+
const item = String(p.item || 'item');
|
|
574
|
+
const collection = rewriteExprPure(inVal, indent);
|
|
575
|
+
const by = rewriteExprPure(byVal, `${indent} `);
|
|
576
|
+
const typeAnnotation = p.type ? `: ${mapTsTypeToPython(String(p.type))}` : '';
|
|
577
|
+
const seenName = `__kern_seen_${name}`;
|
|
578
|
+
const seenObjectsName = `__kern_seen_objects_${name}`;
|
|
579
|
+
const keyName = `__kern_key_${name}`;
|
|
580
|
+
const seenKeyName = `__kern_seen_key_${name}`;
|
|
581
|
+
const seenObjectName = `__kern_seen_object_${name}`;
|
|
582
|
+
lines.push(...collection.hoists);
|
|
583
|
+
lines.push(`${indent}${name}${typeAnnotation} = []`);
|
|
584
|
+
lines.push(`${indent}${seenName} = set()`);
|
|
585
|
+
lines.push(`${indent}${seenObjectsName} = []`);
|
|
586
|
+
lines.push(`${indent}for ${item} in ${collection.expr}:`);
|
|
587
|
+
lines.push(...by.hoists);
|
|
588
|
+
lines.push(`${indent} ${keyName} = ${by.expr}`);
|
|
589
|
+
lines.push(`${indent} if ${keyName} is None:`);
|
|
590
|
+
lines.push(`${indent} ${seenKeyName} = ("null", None)`);
|
|
591
|
+
lines.push(`${indent} elif isinstance(${keyName}, bool):`);
|
|
592
|
+
lines.push(`${indent} ${seenKeyName} = ("boolean", ${keyName})`);
|
|
593
|
+
lines.push(`${indent} elif isinstance(${keyName}, float) and ${keyName} != ${keyName}:`);
|
|
594
|
+
lines.push(`${indent} ${seenKeyName} = ("number", "NaN")`);
|
|
595
|
+
lines.push(`${indent} elif isinstance(${keyName}, (int, float)):`);
|
|
596
|
+
lines.push(`${indent} ${seenKeyName} = ("number", ${keyName})`);
|
|
597
|
+
lines.push(`${indent} elif isinstance(${keyName}, str):`);
|
|
598
|
+
lines.push(`${indent} ${seenKeyName} = ("string", ${keyName})`);
|
|
599
|
+
lines.push(`${indent} else:`);
|
|
600
|
+
lines.push(`${indent} for ${seenObjectName} in ${seenObjectsName}:`);
|
|
601
|
+
lines.push(`${indent} if ${keyName} is ${seenObjectName}:`);
|
|
602
|
+
lines.push(`${indent} break`);
|
|
603
|
+
lines.push(`${indent} else:`);
|
|
604
|
+
lines.push(`${indent} ${seenObjectsName}.append(${keyName})`);
|
|
605
|
+
lines.push(`${indent} ${name}.append(${item})`);
|
|
606
|
+
lines.push(`${indent} continue`);
|
|
607
|
+
lines.push(`${indent} if ${seenKeyName} not in ${seenName}:`);
|
|
608
|
+
lines.push(`${indent} ${seenName}.add(${seenKeyName})`);
|
|
609
|
+
lines.push(`${indent} ${name}.append(${item})`);
|
|
610
|
+
break;
|
|
611
|
+
}
|
|
612
|
+
case 'groupBy': {
|
|
613
|
+
const name = toSnakeCase(String(p.name || ''));
|
|
614
|
+
if (!name)
|
|
615
|
+
throw new Error("groupBy node requires a 'name' prop");
|
|
616
|
+
const inVal = extractCodeOrString(p.in).trim();
|
|
617
|
+
if (!inVal)
|
|
618
|
+
throw new Error("groupBy node requires an 'in' prop");
|
|
619
|
+
const byVal = extractCodeOrString(p.by).trim();
|
|
620
|
+
if (!byVal)
|
|
621
|
+
throw new Error("groupBy node requires a 'by' prop");
|
|
622
|
+
const item = String(p.item || 'item');
|
|
623
|
+
const collection = rewriteExprPure(inVal, indent);
|
|
624
|
+
const by = rewriteExprPure(byVal, `${indent} `);
|
|
625
|
+
const typeAnnotation = p.type ? `: ${mapTsTypeToPython(String(p.type))}` : '';
|
|
626
|
+
const keyName = `__kern_key_${name}`;
|
|
627
|
+
lines.push(...collection.hoists);
|
|
628
|
+
lines.push(`${indent}${name}${typeAnnotation} = {}`);
|
|
629
|
+
lines.push(`${indent}for ${item} in ${collection.expr}:`);
|
|
630
|
+
lines.push(...by.hoists);
|
|
631
|
+
lines.push(`${indent} ${keyName} = ${by.expr}`);
|
|
632
|
+
pushJsObjectKeyCoercion(lines, `${indent} `, keyName);
|
|
633
|
+
lines.push(`${indent} ${name}.setdefault(${keyName}, []).append(${item})`);
|
|
634
|
+
break;
|
|
635
|
+
}
|
|
636
|
+
case 'partition': {
|
|
637
|
+
const passName = toPythonBindingName(String(p.pass || ''), 'partition');
|
|
638
|
+
const failName = toPythonBindingName(String(p.fail || ''), 'partition');
|
|
639
|
+
if (!passName || !failName)
|
|
640
|
+
throw new Error("partition node requires 'pass' and 'fail' props");
|
|
641
|
+
const inVal = extractCodeOrString(p.in).trim();
|
|
642
|
+
if (!inVal)
|
|
643
|
+
throw new Error("partition node requires an 'in' prop");
|
|
644
|
+
const whereVal = extractCodeOrString(p.where).trim();
|
|
645
|
+
if (!whereVal)
|
|
646
|
+
throw new Error("partition node requires a 'where' prop");
|
|
647
|
+
const item = String(p.item || 'item');
|
|
648
|
+
const collection = rewriteExprPure(inVal, indent);
|
|
649
|
+
const where = rewriteExprPure(whereVal, `${indent} `);
|
|
650
|
+
const elemType = p.type ? mapTsTypeToPython(String(p.type)) : undefined;
|
|
651
|
+
const typeAnnotation = elemType ? `: list[${elemType}]` : '';
|
|
652
|
+
lines.push(...collection.hoists);
|
|
653
|
+
lines.push(`${indent}${passName}${typeAnnotation} = []`);
|
|
654
|
+
lines.push(`${indent}${failName}${typeAnnotation} = []`);
|
|
655
|
+
lines.push(`${indent}for ${item} in ${collection.expr}:`);
|
|
656
|
+
lines.push(...where.hoists);
|
|
657
|
+
lines.push(`${indent} if ${where.expr}:`);
|
|
658
|
+
lines.push(`${indent} ${passName}.append(${item})`);
|
|
659
|
+
lines.push(`${indent} else:`);
|
|
660
|
+
lines.push(`${indent} ${failName}.append(${item})`);
|
|
661
|
+
break;
|
|
662
|
+
}
|
|
663
|
+
case 'indexBy': {
|
|
664
|
+
const name = toPythonBindingName(String(p.name || ''), 'indexBy');
|
|
665
|
+
if (!name)
|
|
666
|
+
throw new Error("indexBy node requires a 'name' prop");
|
|
667
|
+
const inVal = extractCodeOrString(p.in).trim();
|
|
668
|
+
if (!inVal)
|
|
669
|
+
throw new Error("indexBy node requires an 'in' prop");
|
|
670
|
+
const byVal = extractCodeOrString(p.by).trim();
|
|
671
|
+
if (!byVal)
|
|
672
|
+
throw new Error("indexBy node requires a 'by' prop");
|
|
673
|
+
const item = String(p.item || 'item');
|
|
674
|
+
const collection = rewriteExprPure(inVal, indent);
|
|
675
|
+
const by = rewriteExprPure(byVal, `${indent} `);
|
|
676
|
+
const typeAnnotation = p.type ? `: ${mapTsTypeToPython(String(p.type))}` : '';
|
|
677
|
+
const keyName = `__kern_key_${name}`;
|
|
678
|
+
lines.push(...collection.hoists);
|
|
679
|
+
lines.push(`${indent}${name}${typeAnnotation} = {}`);
|
|
680
|
+
lines.push(`${indent}for ${item} in ${collection.expr}:`);
|
|
681
|
+
lines.push(...by.hoists);
|
|
682
|
+
lines.push(`${indent} ${keyName} = ${by.expr}`);
|
|
683
|
+
pushJsObjectKeyCoercion(lines, `${indent} `, keyName);
|
|
684
|
+
lines.push(`${indent} ${name}[${keyName}] = ${item}`);
|
|
685
|
+
break;
|
|
686
|
+
}
|
|
687
|
+
case 'countBy': {
|
|
688
|
+
const name = toPythonBindingName(String(p.name || ''), 'countBy');
|
|
689
|
+
if (!name)
|
|
690
|
+
throw new Error("countBy node requires a 'name' prop");
|
|
691
|
+
const inVal = extractCodeOrString(p.in).trim();
|
|
692
|
+
if (!inVal)
|
|
693
|
+
throw new Error("countBy node requires an 'in' prop");
|
|
694
|
+
const byVal = extractCodeOrString(p.by).trim();
|
|
695
|
+
if (!byVal)
|
|
696
|
+
throw new Error("countBy node requires a 'by' prop");
|
|
697
|
+
const item = String(p.item || 'item');
|
|
698
|
+
const collection = rewriteExprPure(inVal, indent);
|
|
699
|
+
const by = rewriteExprPure(byVal, `${indent} `);
|
|
700
|
+
const typeAnnotation = p.type ? `: ${mapTsTypeToPython(String(p.type))}` : '';
|
|
701
|
+
const keyName = `__kern_key_${name}`;
|
|
702
|
+
lines.push(...collection.hoists);
|
|
703
|
+
lines.push(`${indent}${name}${typeAnnotation} = {}`);
|
|
704
|
+
lines.push(`${indent}for ${item} in ${collection.expr}:`);
|
|
705
|
+
lines.push(...by.hoists);
|
|
706
|
+
lines.push(`${indent} ${keyName} = ${by.expr}`);
|
|
707
|
+
pushJsObjectKeyCoercion(lines, `${indent} `, keyName);
|
|
708
|
+
lines.push(`${indent} ${name}[${keyName}] = ${name}.get(${keyName}, 0) + 1`);
|
|
709
|
+
break;
|
|
710
|
+
}
|
|
304
711
|
case 'effect': {
|
|
305
712
|
const effectName = toSnakeCase(String(p.name || 'effect'));
|
|
306
713
|
const triggerNode = getFirstChild(child, 'trigger');
|
|
@@ -425,11 +832,29 @@ export function emitPureHandlers(serverNode, imports, root) {
|
|
|
425
832
|
const PORTABLE_TYPES = new Set([
|
|
426
833
|
'derive',
|
|
427
834
|
'guard',
|
|
835
|
+
'filter',
|
|
428
836
|
'handler',
|
|
429
837
|
'respond',
|
|
430
838
|
'branch',
|
|
431
839
|
'each',
|
|
432
840
|
'collect',
|
|
841
|
+
'count',
|
|
842
|
+
'compact',
|
|
843
|
+
'pluck',
|
|
844
|
+
'take',
|
|
845
|
+
'drop',
|
|
846
|
+
'sort',
|
|
847
|
+
'objectMerge',
|
|
848
|
+
'objectOmit',
|
|
849
|
+
'objectPick',
|
|
850
|
+
'objectKeys',
|
|
851
|
+
'objectValues',
|
|
852
|
+
'objectEntries',
|
|
853
|
+
'uniqueBy',
|
|
854
|
+
'groupBy',
|
|
855
|
+
'partition',
|
|
856
|
+
'indexBy',
|
|
857
|
+
'countBy',
|
|
433
858
|
'effect',
|
|
434
859
|
'assign',
|
|
435
860
|
'do',
|