Skip to content

Commit c67f3d9

Browse files
committed
ifの条件やmatchのターゲットとパターンでbreakできないように
1 parent c1695fd commit c67f3d9

File tree

3 files changed

+88
-3
lines changed

3 files changed

+88
-3
lines changed

src/interpreter/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ export class Interpreter {
367367
case 'if': {
368368
const cond = await this._eval(node.cond, scope, callStack);
369369
if (isControl(cond)) {
370-
return unWrapLabeledBreak(cond, node.label);
370+
return cond;
371371
}
372372
assertBoolean(cond);
373373
if (cond.value) {
@@ -392,7 +392,7 @@ export class Interpreter {
392392
case 'match': {
393393
const about = await this._eval(node.about, scope, callStack);
394394
if (isControl(about)) {
395-
return unWrapLabeledBreak(about, node.label);
395+
return about;
396396
}
397397
for (const qa of node.qs) {
398398
const q = await this._eval(qa.q, scope, callStack);

src/parser/plugins/validate-jump-statements.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,41 @@ function validateNode(node: Ast.Node, ancestors: Ast.Node[]): Ast.Node {
4545
throw new AiScriptSyntaxError(`label "${node.label}" is not defined`, node.loc.start);
4646
}
4747
throw new AiScriptSyntaxError('unlabeled break must be inside for / each / while / do-while / loop', node.loc.start);
48-
} else if (node.expr != null) {
48+
}
49+
50+
switch (block.type) {
51+
case 'each': {
52+
if (ancestors.includes(block.items)) {
53+
throw new AiScriptSyntaxError('break corresponding to each is not allowed in the target', node.loc.start);
54+
}
55+
break;
56+
}
57+
case 'for': {
58+
if (block.times != null && ancestors.includes(block.times)) {
59+
throw new AiScriptSyntaxError('break corresponding to for is not allowed in the count', node.loc.start);
60+
} else if (ancestors.some((ancestor) => ancestor === block.from || ancestor === block.to)) {
61+
throw new AiScriptSyntaxError('break corresponding to for is not allowed in the range', node.loc.start);
62+
}
63+
break;
64+
}
65+
case 'if': {
66+
if (ancestors.includes(block.cond)) {
67+
throw new AiScriptSyntaxError('break corresponding to if is not allowed in the condition', node.loc.start);
68+
}
69+
break;
70+
}
71+
case 'match':{
72+
if (ancestors.includes(block.about)) {
73+
throw new AiScriptSyntaxError('break corresponding to match is not allowed in the target', node.loc.start);
74+
}
75+
if (block.qs.some(({ q }) => ancestors.includes(q))) {
76+
throw new AiScriptSyntaxError('break corresponding to match is not allowed in the pattern', node.loc.start);
77+
}
78+
break;
79+
}
80+
}
81+
82+
if (node.expr != null) {
4983
switch (block.type) {
5084
case 'if':
5185
case 'match':

test/jump-statements.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,57 @@ describe('break', () => {
673673
);
674674
});
675675

676+
test.concurrent('break corresponding to each is not allowed in the target', async () => {
677+
await assert.rejects(
678+
() => exe('#l: each let i, eval { break #l } {}'),
679+
new AiScriptSyntaxError('break corresponding to each is not allowed in the target', { line: 1, column: 24 }),
680+
);
681+
});
682+
683+
test.concurrent('break corresponding to for is not allowed in the count', async () => {
684+
await assert.rejects(
685+
() => exe('#l: for eval { break #l } {}'),
686+
new AiScriptSyntaxError('break corresponding to for is not allowed in the count', { line: 1, column: 16 }),
687+
);
688+
});
689+
690+
describe('break corresponding to for is not allowed in the range', () => {
691+
test.concurrent('from', async () => {
692+
await assert.rejects(
693+
() => exe('#l: for let i = eval { break #l }, 0 {}'),
694+
new AiScriptSyntaxError('break corresponding to for is not allowed in the range', { line: 1, column: 24 }),
695+
);
696+
});
697+
698+
test.concurrent('to', async () => {
699+
await assert.rejects(
700+
() => exe('#l: for let i = 0, eval { break #l } {}'),
701+
new AiScriptSyntaxError('break corresponding to for is not allowed in the range', { line: 1, column: 27 }),
702+
);
703+
});
704+
});
705+
706+
test.concurrent('break corresponding to if is not allowed in the condition', async () => {
707+
await assert.rejects(
708+
() => exe('#l: if eval { break #l } {}'),
709+
new AiScriptSyntaxError('break corresponding to if is not allowed in the condition', { line: 1, column: 15 }),
710+
);
711+
});
712+
713+
test.concurrent('break corresponding to match is not allowed in the target', async () => {
714+
await assert.rejects(
715+
() => exe('#l: match eval { break #l } {}'),
716+
new AiScriptSyntaxError('break corresponding to match is not allowed in the target', { line: 1, column: 18 }),
717+
);
718+
});
719+
720+
test.concurrent('break corresponding to match is not allowed in the pattern', async () => {
721+
await assert.rejects(
722+
() => exe('#l: match 0 { case eval { break #l } => 1 }'),
723+
new AiScriptSyntaxError('break corresponding to match is not allowed in the pattern', { line: 1, column: 27 }),
724+
);
725+
});
726+
676727
describe('labeled each', () => {
677728
test.concurrent('inner each', async () => {
678729
const res = await exe(`

0 commit comments

Comments
 (0)