From 32b7f6430d205abd5d6785325ab65b52d4d42006 Mon Sep 17 00:00:00 2001 From: Dang Hoang Duy <128918146+DiligentPenguinn@users.noreply.github.com> Date: Sun, 18 Feb 2024 17:23:20 +0800 Subject: [PATCH] Nested break/continue statement detection fixes (#1545) * Check for break/continue statements in child blocks * Update tests --------- Co-authored-by: Richard Dominick <34370238+RichDom2185@users.noreply.github.com> --- .../__snapshots__/cse-machine.ts.snap | 60 +++++++++++++++++++ src/cse-machine/__tests__/cse-machine.ts | 52 ++++++++++++++++ src/cse-machine/utils.ts | 8 ++- 3 files changed, 118 insertions(+), 2 deletions(-) diff --git a/src/cse-machine/__tests__/__snapshots__/cse-machine.ts.snap b/src/cse-machine/__tests__/__snapshots__/cse-machine.ts.snap index 4a58dff15..1c66d16cd 100644 --- a/src/cse-machine/__tests__/__snapshots__/cse-machine.ts.snap +++ b/src/cse-machine/__tests__/__snapshots__/cse-machine.ts.snap @@ -330,6 +330,66 @@ f(5000, 5000);", } `; +exports[`breaks, continues are properly detected in child blocks 1: expectResult 1`] = ` +Object { + "alertResult": Array [], + "code": "let i = 0; +for (i = 1; i < 5; i = i + 1) { + { + const a = i; + if (i === 1) { + continue; + } + } + + { + const a = i; + if (i === 2) { + break; + } + } +} +i;", + "displayResult": Array [], + "numErrors": 0, + "parsedErrors": "", + "result": 2, + "resultStatus": "finished", + "visualiseListResult": Array [], +} +`; + +exports[`breaks, continues are properly detected in child blocks 2: expectResult 1`] = ` +Object { + "alertResult": Array [], + "code": "let a = 0; +for (let i = 1; i < 5; i = i + 1) { + { + const x = 0; + a = i; + if (i === 1) { + continue; + } + } + + { + const x = 0; + a = i; + if (i === 2) { + break; + } + } +} +a;", + "displayResult": Array [], + "numErrors": 0, + "parsedErrors": "", + "result": 2, + "resultStatus": "finished", + "visualiseListResult": Array [], +} +`; + exports[`const uses block scoping instead of function scoping: expectResult 1`] = ` Object { "alertResult": Array [], diff --git a/src/cse-machine/__tests__/cse-machine.ts b/src/cse-machine/__tests__/cse-machine.ts index 4cab099c4..e42d806f5 100644 --- a/src/cse-machine/__tests__/cse-machine.ts +++ b/src/cse-machine/__tests__/cse-machine.ts @@ -469,3 +469,55 @@ test('Environment reset is inserted when only instructions are in control stack' optionEC3 ).toMatchInlineSnapshot(`undefined`) }) + +test('breaks, continues are properly detected in child blocks 1', () => { + return expectResult( + stripIndent` + let i = 0; + for (i = 1; i < 5; i = i + 1) { + { + const a = i; + if (i === 1) { + continue; + } + } + + { + const a = i; + if (i === 2) { + break; + } + } + } + i; + `, + optionEC3 + ).toMatchInlineSnapshot(`2`) +}) + +test('breaks, continues are properly detected in child blocks 2', () => { + return expectResult( + stripIndent` + let a = 0; + for (let i = 1; i < 5; i = i + 1) { + { + const x = 0; + a = i; + if (i === 1) { + continue; + } + } + + { + const x = 0; + a = i; + if (i === 2) { + break; + } + } + } + a; + `, + optionEC3 + ).toMatchInlineSnapshot(`2`) +}) diff --git a/src/cse-machine/utils.ts b/src/cse-machine/utils.ts index 1d6ab0b73..ec264cac5 100644 --- a/src/cse-machine/utils.ts +++ b/src/cse-machine/utils.ts @@ -572,7 +572,7 @@ export const hasBreakStatementIf = (statement: es.IfStatement): boolean => { } /** - * Checks whether a block has a `break` statement. + * Checks whether a block OR any of its child blocks has a `break` statement. * @param body The block to be checked * @return `true` if there is a `break` statement, else `false`. */ @@ -584,6 +584,8 @@ export const hasBreakStatement = (block: es.BlockStatement): boolean => { } else if (isIfStatement(statement)) { // Parser enforces that if/else have braces (block statement) hasBreak = hasBreak || hasBreakStatementIf(statement as es.IfStatement) + } else if (isBlockStatement(statement)) { + hasBreak = hasBreak || hasBreakStatement(statement as es.BlockStatement) } } return hasBreak @@ -604,7 +606,7 @@ export const hasContinueStatementIf = (statement: es.IfStatement): boolean => { } /** - * Checks whether a block has a `continue` statement. + * Checks whether a block OR any of its child blocks has a `continue` statement. * @param body The block to be checked * @return `true` if there is a `continue` statement, else `false`. */ @@ -616,6 +618,8 @@ export const hasContinueStatement = (block: es.BlockStatement): boolean => { } else if (isIfStatement(statement)) { // Parser enforces that if/else have braces (block statement) hasContinue = hasContinue || hasContinueStatementIf(statement as es.IfStatement) + } else if (isBlockStatement(statement)) { + hasContinue = hasContinue || hasContinueStatement(statement as es.BlockStatement) } } return hasContinue