mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-24 06:14:01 +08:00
183 lines
4.2 KiB
TypeScript
183 lines
4.2 KiB
TypeScript
// Labeled statements: labeled break/continue on every loop shape, labeled
|
|
// switch break, labeled blocks, multi-label chains, and unlabeled jumps
|
|
// inside labeled constructs binding to their own innermost targets.
|
|
|
|
// Labeled break out of a nested for from the outer while.
|
|
let hits = 0;
|
|
outer: while (true) {
|
|
for (let i = 0; i < 10; i = i + 1) {
|
|
hits = hits + 1;
|
|
if (i === 3) {
|
|
break outer;
|
|
}
|
|
}
|
|
}
|
|
console.log("break outer from for:", hits);
|
|
|
|
// Labeled continue of the OUTER loop: skips the rest of the outer body.
|
|
let picked = "";
|
|
rows: for (let r = 0; r < 4; r = r + 1) {
|
|
for (let c = 0; c < 4; c = c + 1) {
|
|
if (c > r) {
|
|
continue rows;
|
|
}
|
|
picked = picked + r + "" + c + " ";
|
|
}
|
|
picked = picked + "| ";
|
|
}
|
|
console.log("continue rows:", picked);
|
|
|
|
// A label chain: both names target the same loop.
|
|
let chain = 0;
|
|
target1: target2: while (true) {
|
|
chain = chain + 1;
|
|
if (chain === 2) {
|
|
break target1;
|
|
}
|
|
}
|
|
console.log("chain via target1:", chain);
|
|
target3: target4: while (true) {
|
|
chain = chain + 1;
|
|
break target4;
|
|
}
|
|
console.log("chain via target4:", chain);
|
|
|
|
// Labeled continue on a labeled while from inside a switch: the switch is
|
|
// crossed (Node runs the loop again), and labeled break exits the loop.
|
|
let mode = 0;
|
|
let trace = "";
|
|
pump: while (true) {
|
|
switch (mode) {
|
|
case 0:
|
|
mode = 1;
|
|
trace = trace + "a";
|
|
continue pump;
|
|
case 1:
|
|
mode = 2;
|
|
trace = trace + "b";
|
|
continue pump;
|
|
default:
|
|
trace = trace + "c";
|
|
break pump;
|
|
}
|
|
}
|
|
console.log("switch pump:", trace, mode);
|
|
|
|
// Labeled SWITCH: `break lbl` names the switch itself.
|
|
const flavor = "kiwi";
|
|
let said = "";
|
|
pick: switch (flavor) {
|
|
case "kiwi":
|
|
said = said + "green ";
|
|
if (said.length > 3) {
|
|
break pick;
|
|
}
|
|
said = said + "unreached ";
|
|
break;
|
|
default:
|
|
said = said + "plain ";
|
|
}
|
|
console.log("labeled switch:", said);
|
|
|
|
// Labeled BLOCK: break exits the block, skipping its tail.
|
|
let acc = "";
|
|
blk: {
|
|
acc = acc + "in ";
|
|
if (acc === "in ") {
|
|
break blk;
|
|
}
|
|
acc = acc + "never ";
|
|
}
|
|
console.log("labeled block:", acc);
|
|
|
|
// Labeled if via break (a labeled non-loop statement).
|
|
let ifTrace = "start";
|
|
guard: if (ifTrace === "start") {
|
|
ifTrace = ifTrace + " body";
|
|
break guard;
|
|
// eslint-disable-next-line no-unreachable
|
|
ifTrace = ifTrace + " never";
|
|
}
|
|
console.log("labeled if:", ifTrace);
|
|
|
|
// Labeled do-while: continue re-evaluates the condition, break exits.
|
|
let dw = 0;
|
|
let dwTrace = "";
|
|
duo: do {
|
|
dw = dw + 1;
|
|
if (dw === 2) {
|
|
continue duo;
|
|
}
|
|
dwTrace = dwTrace + dw + " ";
|
|
if (dw >= 4) {
|
|
break duo;
|
|
}
|
|
} while (dw < 10);
|
|
console.log("do-while:", dwTrace, dw);
|
|
|
|
// Labeled for-of over an array (strings — refcounted elements survive the
|
|
// labeled jumps), from a nested loop.
|
|
const words = ["alpha", "beta", "gamma", "delta"];
|
|
let joined = "";
|
|
scan: for (const w of words) {
|
|
for (let k = 0; k < w.length; k = k + 1) {
|
|
if (w === "gamma") {
|
|
break scan;
|
|
}
|
|
if (k > 1) {
|
|
continue scan;
|
|
}
|
|
joined = joined + w.charAt(k);
|
|
}
|
|
}
|
|
console.log("for-of scan:", joined);
|
|
|
|
// Labeled for-of over a STRING (code-point walk), labeled continue.
|
|
let vowels = "";
|
|
chars: for (const ch of "programmatic") {
|
|
if (ch !== "a" && ch !== "o") {
|
|
continue chars;
|
|
}
|
|
vowels = vowels + ch;
|
|
}
|
|
console.log("string chars:", vowels);
|
|
|
|
// Unlabeled break/continue inside labeled loops bind to the INNERMOST
|
|
// loop, not the labeled one.
|
|
let inner = "";
|
|
shell: for (let i = 0; i < 3; i = i + 1) {
|
|
for (let j = 0; j < 5; j = j + 1) {
|
|
if (j === 1) {
|
|
continue;
|
|
}
|
|
if (j === 3) {
|
|
break;
|
|
}
|
|
inner = inner + i + "" + j + " ";
|
|
}
|
|
if (i === 2) {
|
|
break shell;
|
|
}
|
|
}
|
|
console.log("unlabeled inside labeled:", inner);
|
|
|
|
// Labels are per-function: the same name is fine in a nested function.
|
|
function nested(): number {
|
|
let n = 0;
|
|
outer: while (true) {
|
|
n = n + 1;
|
|
if (n > 2) {
|
|
break outer;
|
|
}
|
|
}
|
|
return n;
|
|
}
|
|
console.log("nested fn label:", nested());
|
|
|
|
// A label nothing jumps to (still must run its statement), including on a
|
|
// plain expression statement.
|
|
tag: console.log("plain labeled statement");
|
|
free: for (let z = 0; z < 2; z = z + 1) {
|
|
console.log("unused label pass", z);
|
|
}
|