feat(steps): a run of calls that happens once per item says so
A body drawn once, with nothing to say it repeats, is a quiet lie about the order — so the reading now reads the loops a site is written inside, the same way it reads its conditions: one climb up the same ancestors, per language, `for` / `foreach` / `for … in` / `while` / `do` / `repeat`, with the header as written (`item of items`, `queue.length > 0`) and where the loop starts. Loops and forks nest in either direction, and neither reading knows about the other, so the block builder merges them by where each construct BEGINS: on one ancestor chain the outer one always starts first, which rebuilds the nesting from the positions alone. A `for` inside an `if` and an `if` inside a `for` come out the way the code has them. With it, the per-framework readings are pinned: an Express handler with its helper drawn inside the reply it builds, a FastAPI `raise HTTPException` ending the arm it is in, a Spring early `return` as the other arm of its `if` (with the comparison flipped, not wrapped), an ASP.NET handler's two outcomes, and a Nest controller read on through the service it delegates to. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01REFyW9hmNrxhwN5wxRoAkC
This commit is contained in:
co-authored by
Claude Opus 5
parent
9acab0020f
commit
7b6704a70d
@@ -9,7 +9,7 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import type { BranchGuard } from '../src/graph/branch-guards';
|
||||
import type { BranchGuard, SiteLoop } from '../src/graph/branch-guards';
|
||||
import { buildProgram, type ProgramInput, type ProgramSite, type WireBlock, type WireItem } from '../src/ui-server/api/program';
|
||||
|
||||
/* ------------------------------------------------------------ material -- */
|
||||
@@ -27,6 +27,11 @@ function at(step: string, guards: BranchGuard[] = [], extra: Partial<ProgramSite
|
||||
return { step, link: `l:${step}`, at: { line, column: 0, end: { line, column: 40 } }, guards, ...extra };
|
||||
}
|
||||
|
||||
/** A loop the site is written inside. */
|
||||
function loop(text: string, kind: SiteLoop['kind'] = 'each', branch = `l:${text}`): SiteLoop {
|
||||
return { text, kind, branch };
|
||||
}
|
||||
|
||||
/** A site that folds into a helper. */
|
||||
function into(fn: string, guards: BranchGuard[] = [], extra: Partial<ProgramSite> = {}): ProgramSite {
|
||||
const line = nextLine++;
|
||||
@@ -235,6 +240,28 @@ describe('buildProgram', () => {
|
||||
expect(shape(p!.root)).toEqual(['together Promise.all', ' a inside Promise.all', ' b inside Promise.all', 'c']);
|
||||
});
|
||||
|
||||
it('says a run of calls happens once per item', () => {
|
||||
const p = program({
|
||||
root: [at('before'), at('each', [], { loops: [loop('item of items')] }), at('after')],
|
||||
});
|
||||
expect(shape(p!.root)).toEqual(['before', 'loop item of items', ' each', 'after']);
|
||||
});
|
||||
|
||||
it('nests a loop and a fork by which one is written outside the other', () => {
|
||||
// `for (…) { if (ready) { go() } }` — the loop starts first, so it is
|
||||
// outside; the guard's own branch position is what decides, not its order
|
||||
// in the chain.
|
||||
const inner = g('ready', { branch: '9:4' });
|
||||
const p = program({ root: [at('go', [inner], { loops: [loop('item of items', 'each', '8:2')] })] });
|
||||
expect(shape(p!.root)).toEqual(['loop item of items', ' if ready', ' arm ready', ' go']);
|
||||
|
||||
// `if (ready) { for (…) { go() } }` — the same two constructs, the other
|
||||
// way round, told apart by where each begins.
|
||||
const outer = g('ready', { branch: '8:2' });
|
||||
const q = program({ root: [at('go', [outer], { loops: [loop('item of items', 'each', '9:4')] })] });
|
||||
expect(shape(q!.root)).toEqual(['if ready', ' arm ready', ' loop item of items', ' go']);
|
||||
});
|
||||
|
||||
it('closes a fork when the code leaves it', () => {
|
||||
const cond = g('ready');
|
||||
const p = program({ root: [at('inside', [cond]), at('after')] });
|
||||
|
||||
Reference in New Issue
Block a user