* Try to finalize a sequence definition. * A sequence is finalized when both base case(s) and recurrence are present.
(ce: ComputeEngine, name: string)
| 590 | const pending = getOrCreatePending(ce, name); |
| 591 | pending.recurrence = { variables, latex: serializeLatex(expr.json) }; |
| 592 | pending.isMultiIndex = true; |
| 593 | tryFinalizeSequence(ce, name); |
| 594 | } |
| 595 | |
| 596 | /** |
| 597 | * Try to finalize a sequence definition. |
| 598 | * A sequence is finalized when both base case(s) and recurrence are present. |
| 599 | */ |
| 600 | function tryFinalizeSequence(ce: ComputeEngine, name: string): void { |
| 601 | const pending = getOrCreatePending(ce, name); |
| 602 | |
| 603 | // Need both base case(s) and recurrence to finalize |
| 604 | if (pending.base.size === 0 || !pending.recurrence) return; |
| 605 | |
| 606 | // Convert to SequenceDefinition format |
| 607 | const base: Record<number | string, Expression> = {}; |
| 608 | for (const [k, v] of pending.base) { |
| 609 | base[k] = v; |
| 610 | } |
| 611 | |
| 612 | // Build definition based on single vs multi-index |
| 613 | const def: SequenceDefinition = { |
| 614 | base, |
| 615 | recurrence: pending.recurrence.latex, // Pass as string for fresh parsing |
| 616 | }; |
| 617 | |
| 618 | if (pending.isMultiIndex || pending.recurrence.variables) { |
| 619 | // Multi-index sequence |
| 620 | def.variables = pending.recurrence.variables; |
| 621 | } else { |
| 622 | // Single-index sequence |
| 623 | def.variable = pending.recurrence.variable; |
| 624 | } |
| 625 | |
| 626 | // Validate the definition |
| 627 | const validation = validateSequenceDefinition(ce, name, def); |
| 628 | if (!validation.valid) { |
| 629 | throw new Error(validation.error); |
| 630 | } |
| 631 | |
| 632 | // Create the subscriptEvaluate handler |
| 633 | const handler = createSequenceHandler(ce, name, def); |
| 634 | |
| 635 | // Check if the symbol already exists in the current scope |
| 636 | // (it may have been auto-declared when parsing the recurrence expression) |
| 637 | const scope = ce.context.lexicalScope; |
| 638 | const existingDef = scope.bindings.get(name); |
| 639 | |
| 640 | if (existingDef) { |
| 641 | // Symbol already exists - update it with subscriptEvaluate |
| 642 | const callableBefore = defIsCallableShaped(existingDef); |
| 643 | updateDef(ce, name, existingDef, { |
| 644 | subscriptEvaluate: handler, |
| 645 | }); |
| 646 | // In-place redefinition of an existing binding is a semantic mutation |
| 647 | // (a structural change, not a value write: the epoch bumps too). |
| 648 | ce._noteStateEvent({ |
| 649 | kind: 'redefine', |
no test coverage detected