Skip to content

Commit

Permalink
1540 stepper last line of reductible code is not evaluated (#1541)
Browse files Browse the repository at this point in the history
* added isReductible

* added test for single line evaluation

* Update nodejs.yml

Workaround for failing dependencies installation

* Revert "Update nodejs.yml"

This reverts commit 8e379e8.

* Update test snapshot

* updated snapshot for empty program

* changed to earlier returns for isStatementsReducible

---------

Co-authored-by: Richard Dominick <[email protected]>
Co-authored-by: Martin Henz <[email protected]>
  • Loading branch information
3 people authored Feb 20, 2024
1 parent 155d913 commit cab2609
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 5 deletions.
5 changes: 4 additions & 1 deletion src/stepper/__tests__/__snapshots__/stepper.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -2095,7 +2095,10 @@ false ? 1 : 100 * factorial(100 - 1);
"
`;

exports[`Evaluation of empty code and imports Evaluate empty program 1`] = `""`;
exports[`Evaluation of empty code and imports Evaluate empty program 1`] = `
"
"
`;

exports[`Infinite recursion 1`] = `
"function f() {
Expand Down
31 changes: 31 additions & 0 deletions src/stepper/__tests__/stepper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,37 @@ const testEvalSteps = (programStr: string, context?: Context) => {
return getEvaluationSteps(program, context, options)
}

describe('Test single line of code is evaluated', () => {
test('Constants Declaration', async () => {
const code = `
const x = 10;
`
const steps = await testEvalSteps(code)
expect(steps.length).toBe(4)
expect(getLastStepAsString(steps)).toEqual('')
})

test('Function Declaration', async () => {
const code = `
function x () {
return 10;
}
`
const steps = await testEvalSteps(code)
expect(steps.length).toBe(4)
expect(getLastStepAsString(steps)).toEqual('')
})

test('Value', async () => {
const code = `
10;
`
const steps = await testEvalSteps(code)
expect(steps.length).toBe(2)
expect(getLastStepAsString(steps)).toEqual('10;')
})
})

test('Test basic substitution', async () => {
const code = `
(1 + 2) * (3 + 4);
Expand Down
27 changes: 23 additions & 4 deletions src/stepper/stepper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,18 @@ function isIrreducible(node: substituterNodes, context: Context) {
)
}

function isStatementsReducible(progs: es.Program, context: Context): boolean {
if (progs.body.length === 0) return false
if (progs.body.length > 1) return true

const [lastStatement] = progs.body

if (lastStatement.type !== 'ExpressionStatement') {
return true
}
return !isIrreducible(lastStatement.expression, context)
}

type irreducibleNodes =
| es.FunctionExpression
| es.ArrowFunctionExpression
Expand Down Expand Up @@ -3218,24 +3230,32 @@ export async function getEvaluationSteps(
// and remove debugger statements.
start = removeDebuggerStatements(start)

// then add in path and explanation string
// then add in path and explanation string and push it into steps
let reducedWithPath: [substituterNodes, Context, string[][], string] = [
start,
context,
[],
'Start of evaluation'
]
steps.push([
reducedWithPath[0] as es.Program,
reducedWithPath[2].length > 1 ? reducedWithPath[2].slice(1) : reducedWithPath[2],
reducedWithPath[3]
])
steps.push([reducedWithPath[0] as es.Program, [], ''])
// reduces program until evaluation completes
// even steps: program before reduction
// odd steps: program after reduction
let i = -1
let i = 1
let limitExceeded = false
while ((reducedWithPath[0] as es.Program).body.length > 0) {
while (isStatementsReducible(reducedWithPath[0] as es.Program, context)) {
//Should work on isReducibleStatement instead of checking body.length
if (steps.length === limit) {
steps[steps.length - 1] = [ast.program([]), [], 'Maximum number of steps exceeded']
limitExceeded = true
break
}
reducedWithPath = reduceMain(reducedWithPath[0], context)
steps.push([
reducedWithPath[0] as es.Program,
reducedWithPath[2].length > 1 ? reducedWithPath[2].slice(1) : reducedWithPath[2],
Expand All @@ -3246,7 +3266,6 @@ export async function getEvaluationSteps(
steps[i][1] = reducedWithPath[2].length > 1 ? [reducedWithPath[2][0]] : reducedWithPath[2]
steps[i][2] = reducedWithPath[3]
}
reducedWithPath = reduceMain(reducedWithPath[0], context)
i += 2
}
if (!limitExceeded && steps.length > 0) {
Expand Down

0 comments on commit cab2609

Please sign in to comment.