( expr: Expression )
| 447 | // Replace '_' with '_1' |
| 448 | const body = expr.subs({ _: '_1' }); |
| 449 | |
| 450 | const params: Expression[] = []; |
| 451 | for (let i = 1; i < 10; i++) |
| 452 | if (body.has(`_${i}`)) |
| 453 | params.push(body.engine.symbol(`_${i}`, { canonical: false })); |
| 454 | |
| 455 | return [body, params]; |
| 456 | } |
| 457 | |
| 458 | /** Assuming that ops has the following form: |
| 459 | * - body |
| 460 | * - ...params |
| 461 | * return a canonical function literal (["Function", body, ...params]) where |
| 462 | * body is potentially wrapped in a Block expression and the arguments are |
| 463 | * declared in the scope of the body. |
| 464 | */ |
| 465 | export function canonicalFunctionLiteralArguments( |
| 466 | ce: ComputeEngine, |
| 467 | ops: ReadonlyArray<Expression> |
| 468 | ): Expression | undefined { |
| 469 | if (ops.length === 0) return undefined; |
| 470 | |
| 471 | // ── The E2 pre-pass (generic-literals design §2.3), ordered FIRST ────────── |
| 472 | // |
| 473 | // A full-signature marker in the body slot is the literal's contract |
| 474 | // of record. It has to be read BEFORE the parameter operands are normalized: |
| 475 | // a hand-authored E2 (and the M2 lowering) may carry `["Typed", x, "'T'"]` |
| 476 | // parameters, and `T` is not a declared type name — type resolution would |
| 477 | // fail on it below. Under the erasure ruling (G1) those annotations are |
| 478 | // redundant anyway: the marker states the parameter types, and quantified |
| 479 | // positions are erased so the body canonicalizes exactly as an untyped |
| 480 | // literal's does. |
| 481 | const erased = eraseGenericParameters(ce, ops); |
| 482 | let genericMarker = false; |
| 483 | if (erased !== undefined) { |
| 484 | // A well-formedness violation is reported in place; otherwise continue with |
| 485 | // the erased operands. |
| 486 | if (erased.error !== undefined) return erased.error; |
| 487 | ops = erased.ops!; |
| 488 | genericMarker = erased.generic === true; |
| 489 | } |
| 490 | |
| 491 | // Signature-string sugar (typed-literals design §3.2/§10): |
| 492 | // `["Function", body, "'(n: integer) -> complex'"]` desugars into the |
| 493 | // structural form — each named argument becomes a `["Typed", name, type]` |
| 494 | // parameter and a non-`unknown` result becomes the body's return-type |
| 495 | // ascription. Applied only when the single parameter operand is a string |
| 496 | // parsing to a signature type with every argument named and no |
| 497 | // optional/variadic markers (those need `makeLambda` arity support); |
| 498 | // anything else falls through to the standard expected-a-symbol error — |
| 499 | // except a string that IS signature-shaped and fails to parse as a type, |
| 500 | // which reports the type parser's own diagnostic (see |
| 501 | // `signatureStringError`) rather than blaming the operand. |
| 502 | if (ops.length === 2 && isString(ops[1])) { |
| 503 | // A `where` clause IS accepted here (generic-literals design §2.2, E1): |
| 504 | // the string parses to a signature carrying `typeParams` and desugars into |
| 505 | // the E2 form — bare symbols at the quantified positions (erasure, G1) and |
| 506 | // the full signature as the body's ascription — which then re-enters this |
no test coverage detected