Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update explicit control evaluator for agenda and stash visualisation #1430

Merged
merged 13 commits into from
Jun 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/__tests__/__snapshots__/environment.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,11 @@ Object {
"head": Object {
"x": 2,
},
"id": "3",
"id": "4",
"name": "f",
"tail": Object {
"head": Object {
"2": [Function],
"3": [Function],
"f": [Function],
},
"id": "1",
Expand Down Expand Up @@ -196,7 +196,7 @@ Object {
"name": "f",
"tail": Object {
"head": Object {
"2": [Function],
"3": [Function],
"f": [Function],
},
"id": "1",
Expand Down Expand Up @@ -293,7 +293,7 @@ Object {
exports[`Function params and body identifiers are in different environment 3`] = `
Object {
"head": Object {
"2": [Function],
"3": [Function],
"f": [Function],
},
"id": Any<String>,
Expand Down
59 changes: 34 additions & 25 deletions src/ec-evaluator/instrCreator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ import {
WhileInstr
} from './types'

export const resetInstr = (): Instr => ({
instrType: InstrType.RESET
export const resetInstr = (srcNode: es.Node): Instr => ({
instrType: InstrType.RESET,
srcNode
})

export const whileInstr = (
Expand Down Expand Up @@ -74,7 +75,7 @@ export const binOpInstr = (symbol: es.BinaryOperator, srcNode: es.Node): BinOpIn
srcNode
})

export const popInstr = (): Instr => ({ instrType: InstrType.POP })
export const popInstr = (srcNode: es.Node): Instr => ({ instrType: InstrType.POP, srcNode })

export const appInstr = (numOfArgs: number, srcNode: es.CallExpression): AppInstr => ({
instrType: InstrType.APPLICATION,
Expand All @@ -93,46 +94,54 @@ export const branchInstr = (
srcNode
})

export const envInstr = (env: Environment): EnvInstr => ({
export const envInstr = (env: Environment, srcNode: es.Node): EnvInstr => ({
instrType: InstrType.ENVIRONMENT,
env
env,
srcNode
})

export const pushUndefIfNeededInstr = (): Instr => ({
instrType: InstrType.PUSH_UNDEFINED_IF_NEEDED
export const pushUndefIfNeededInstr = (srcNode: es.Node): Instr => ({
instrType: InstrType.PUSH_UNDEFINED_IF_NEEDED,
srcNode
})

export const arrLitInstr = (arity: number): ArrLitInstr => ({
export const arrLitInstr = (arity: number, srcNode: es.Node): ArrLitInstr => ({
instrType: InstrType.ARRAY_LITERAL,
arity
arity,
srcNode
})

export const arrAccInstr = (): Instr => ({
instrType: InstrType.ARRAY_ACCESS
export const arrAccInstr = (srcNode: es.Node): Instr => ({
instrType: InstrType.ARRAY_ACCESS,
srcNode
})

export const arrAssmtInstr = (): Instr => ({
instrType: InstrType.ARRAY_ASSIGNMENT
export const arrAssmtInstr = (srcNode: es.Node): Instr => ({
instrType: InstrType.ARRAY_ASSIGNMENT,
srcNode
})

export const markerInstr = (): Instr => ({
instrType: InstrType.MARKER
export const markerInstr = (srcNode: es.Node): Instr => ({
instrType: InstrType.MARKER,
srcNode
})

export const contInstr = (): Instr => ({
instrType: InstrType.CONTINUE
export const contInstr = (srcNode: es.Node): Instr => ({
instrType: InstrType.CONTINUE,
srcNode
})

export const contMarkerInstr = (): Instr => ({
instrType: InstrType.CONTINUE_MARKER
export const contMarkerInstr = (srcNode: es.Node): Instr => ({
instrType: InstrType.CONTINUE_MARKER,
srcNode
})

export const breakInstr = (): Instr => ({
instrType: InstrType.BREAK
export const breakInstr = (srcNode: es.Node): Instr => ({
instrType: InstrType.BREAK,
srcNode
})

export const breakMarkerInstr = (): Instr => ({
instrType: InstrType.BREAK_MARKER
export const breakMarkerInstr = (srcNode: es.Node): Instr => ({
instrType: InstrType.BREAK_MARKER,
srcNode
})

// export const breakMarkerInstr = (): IInstr => ({ instrType: InstrTypes.BREAK_MARKER })
77 changes: 44 additions & 33 deletions src/ec-evaluator/interpreter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import { checkEditorBreakpoints } from '../stdlib/inspector'
import { Context, ContiguousArrayElements, Result, Value } from '../types'
import * as ast from '../utils/astCreator'
import { dummyStatement } from '../utils/dummyAstCreator'
import { evaluateBinaryExpression, evaluateUnaryExpression } from '../utils/operators'
import * as rttc from '../utils/rttc'
import * as instr from './instrCreator'
Expand Down Expand Up @@ -49,7 +50,6 @@
declareFunctionsAndVariables,
declareIdentifier,
defineVariable,
envChanging,
getVariable,
handleRuntimeError,
handleSequence,
Expand All @@ -71,7 +71,7 @@
public constructor(program: es.Program) {
super()
// Evaluation of last statement is undefined if stash is empty
this.push(instr.pushUndefIfNeededInstr())
this.push(instr.pushUndefIfNeededInstr(dummyStatement()))

// Load program into agenda stack
this.push(program)
Expand Down Expand Up @@ -193,7 +193,7 @@
* @returns The corresponding promise.
*/
export function ECEResultPromise(context: Context, value: Value): Promise<Result> {
return new Promise((resolve, reject) => {

Check warning on line 196 in src/ec-evaluator/interpreter.ts

View workflow job for this annotation

GitHub Actions / build (16.x)

'reject' is defined but never used. Allowed unused args must match /^_/u
if (value instanceof ECEBreak) {
resolve({ status: 'suspended-ec-eval', context })
} else if (value instanceof ECError) {
Expand All @@ -215,25 +215,34 @@
function runECEMachine(context: Context, agenda: Agenda, stash: Stash, isPrelude: boolean) {
context.runtime.break = false
context.runtime.nodes = []
let steps = 0
let steps = 1

let command = agenda.peek()

// First node will be a Program
context.runtime.nodes.unshift(command as es.Program)

let command = agenda.pop()
while (command) {
if (!isPrelude && steps === context.runtime.envSteps) {
return stash.peek()
}
if (envChanging(command)) {
steps += 1
}

// Temporarily commented out the conditional step increases for agenda stash viz development
// May be handy later in case we want to separate the visualizations
// if (envChanging(command)) {
// steps += 1
// }

if (isNode(command) && command.type === 'DebuggerStatement') {
steps += 1
// steps += 1

// Record debugger step if running for the first time
if (context.runtime.envSteps === -1) {
context.runtime.breakpointSteps.push(steps)
}
}

agenda.pop()
if (isNode(command)) {
context.runtime.nodes.shift()
context.runtime.nodes.unshift(command)
Expand All @@ -250,8 +259,10 @@
// Command is an instrucion
cmdEvaluators[command.instrType](command, context, agenda, stash, isPrelude)
}
command = agenda.pop()
command = agenda.peek()
steps += 1
}

if (!isPrelude) {
context.runtime.envStepsTotal = steps
}
Expand All @@ -267,7 +278,7 @@
* Statements
*/

Program: function (command: es.BlockStatement, context: Context, agenda: Agenda, stash: Stash) {

Check warning on line 281 in src/ec-evaluator/interpreter.ts

View workflow job for this annotation

GitHub Actions / build (16.x)

'stash' is defined but never used. Allowed unused args must match /^_/u
const environment = createBlockEnvironment(context, 'programEnvironment')
// Push the environment only if it is non empty.
if (declareFunctionsAndVariables(context, command, environment)) {
Expand All @@ -280,7 +291,7 @@

BlockStatement: function (command: es.BlockStatement, context: Context, agenda: Agenda) {
// To restore environment after block ends
agenda.push(instr.envInstr(currentEnvironment(context)))
agenda.push(instr.envInstr(currentEnvironment(context), command))

const environment = createBlockEnvironment(context, 'blockEnvironment')
// Push the environment only if it is non empty.
Expand All @@ -293,7 +304,7 @@
},

WhileStatement: function (command: es.WhileStatement, context: Context, agenda: Agenda) {
agenda.push(instr.breakMarkerInstr())
agenda.push(instr.breakMarkerInstr(command))
agenda.push(instr.whileInstr(command.test, command.body, command))
agenda.push(command.test)
agenda.push(ast.identifier('undefined')) // Return undefined if there is no loop execution
Expand All @@ -316,7 +327,7 @@
[
init,
ast.forStatement(
ast.assignmentExpression(id, valueExpression),
ast.assignmentExpression(id, valueExpression, command.loc),
test,
update,
ast.blockStatement(
Expand Down Expand Up @@ -357,16 +368,16 @@
)
)
} else {
agenda.push(instr.breakMarkerInstr())
agenda.push(instr.breakMarkerInstr(command))
agenda.push(instr.forInstr(init, test, update, command.body, command))
agenda.push(test)
agenda.push(instr.popInstr()) // Pop value from init assignment
agenda.push(instr.popInstr(command)) // Pop value from init assignment
agenda.push(init)
agenda.push(ast.identifier('undefined')) // Return undefined if there is no loop execution
agenda.push(ast.identifier('undefined', command.loc)) // Return undefined if there is no loop execution
}
},

IfStatement: function (command: es.IfStatement, context: Context, agenda: Agenda, stash: Stash) {

Check warning on line 380 in src/ec-evaluator/interpreter.ts

View workflow job for this annotation

GitHub Actions / build (16.x)

'stash' is defined but never used. Allowed unused args must match /^_/u
agenda.push(...reduceConditional(command))
},

Expand All @@ -393,7 +404,7 @@
// Parser enforces initialisation during variable declaration
const init = declaration.init!

agenda.push(instr.popInstr())
agenda.push(instr.popInstr(command))
agenda.push(instr.assmtInstr(id.name, command.kind === 'const', true, command))
agenda.push(init)
},
Expand All @@ -419,7 +430,7 @@

ReturnStatement: function (command: es.ReturnStatement, context: Context, agenda: Agenda) {
// Push return argument onto agenda as well as Reset Instruction to clear to ignore all statements after the return.
agenda.push(instr.resetInstr())
agenda.push(instr.resetInstr(command))
if (command.argument) {
agenda.push(command.argument)
}
Expand All @@ -429,18 +440,18 @@
command: es.ContinueStatement,
context: Context,
agenda: Agenda,
stash: Stash

Check warning on line 443 in src/ec-evaluator/interpreter.ts

View workflow job for this annotation

GitHub Actions / build (16.x)

'stash' is defined but never used. Allowed unused args must match /^_/u
) {
agenda.push(instr.contInstr())
agenda.push(instr.contInstr(command))
},

BreakStatement: function (
command: es.BreakStatement,
context: Context,
agenda: Agenda,
stash: Stash

Check warning on line 452 in src/ec-evaluator/interpreter.ts

View workflow job for this annotation

GitHub Actions / build (16.x)

'stash' is defined but never used. Allowed unused args must match /^_/u
) {
agenda.push(instr.breakInstr())
agenda.push(instr.breakInstr(command))
},
ImportDeclaration: function () {
throw new Error('Import Declarations should already have been removed.')
Expand All @@ -460,7 +471,7 @@
agenda: Agenda
) {
if (command.left.type === 'MemberExpression') {
agenda.push(instr.arrAssmtInstr())
agenda.push(instr.arrAssmtInstr(command))
agenda.push(command.right)
agenda.push(command.left.property)
agenda.push(command.left.object)
Expand All @@ -475,7 +486,7 @@
const elems = command.elements as ContiguousArrayElements
const len = elems.length

agenda.push(instr.arrLitInstr(len))
agenda.push(instr.arrLitInstr(len, command))
for (const elem of elems) {
agenda.push(elem)
}
Expand All @@ -485,9 +496,9 @@
command: es.MemberExpression,
context: Context,
agenda: Agenda,
stash: Stash

Check warning on line 499 in src/ec-evaluator/interpreter.ts

View workflow job for this annotation

GitHub Actions / build (16.x)

'stash' is defined but never used. Allowed unused args must match /^_/u
) {
agenda.push(instr.arrAccInstr())
agenda.push(instr.arrAccInstr(command))
agenda.push(command.property)
agenda.push(command.object)
},
Expand All @@ -496,7 +507,7 @@
command: es.ConditionalExpression,
context: Context,
agenda: Agenda,
stash: Stash

Check warning on line 510 in src/ec-evaluator/interpreter.ts

View workflow job for this annotation

GitHub Actions / build (16.x)

'stash' is defined but never used. Allowed unused args must match /^_/u
) {
agenda.push(...reduceConditional(command))
},
Expand Down Expand Up @@ -567,11 +578,11 @@
* Instructions
*/

[InstrType.RESET]: function (command: Instr, context: Context, agenda: Agenda, stash: Stash) {

Check warning on line 581 in src/ec-evaluator/interpreter.ts

View workflow job for this annotation

GitHub Actions / build (16.x)

'stash' is defined but never used. Allowed unused args must match /^_/u
// Keep pushing reset instructions until marker is found.
const cmdNext: AgendaItem | undefined = agenda.pop()
if (cmdNext && (isNode(cmdNext) || cmdNext.instrType !== InstrType.MARKER)) {
agenda.push(instr.resetInstr())
agenda.push(instr.resetInstr(command.srcNode))
}
},

Expand All @@ -592,10 +603,10 @@
if (test) {
agenda.push(command)
agenda.push(command.test)
agenda.push(instr.contMarkerInstr())
agenda.push(instr.pushUndefIfNeededInstr()) // The loop returns undefined if the stash is empty
agenda.push(instr.contMarkerInstr(command.srcNode))
agenda.push(instr.pushUndefIfNeededInstr(command.srcNode)) // The loop returns undefined if the stash is empty
agenda.push(command.body)
agenda.push(instr.popInstr()) // Pop previous body value
agenda.push(instr.popInstr(command.srcNode)) // Pop previous body value
}
},

Expand All @@ -611,12 +622,12 @@
if (test) {
agenda.push(command)
agenda.push(command.test)
agenda.push(instr.popInstr()) // Pop value from update
agenda.push(instr.popInstr(command.srcNode)) // Pop value from update
agenda.push(command.update)
agenda.push(instr.contMarkerInstr())
agenda.push(instr.pushUndefIfNeededInstr()) // The loop returns undefined if the stash is empty
agenda.push(instr.contMarkerInstr(command.srcNode))
agenda.push(instr.pushUndefIfNeededInstr(command.srcNode)) // The loop returns undefined if the stash is empty
agenda.push(command.body)
agenda.push(instr.popInstr()) // Pop previous body value
agenda.push(instr.popInstr(command.srcNode)) // Pop previous body value
}
},

Expand Down Expand Up @@ -709,13 +720,13 @@
const next = agenda.peek()
if (!next || (!isNode(next) && next.instrType === InstrType.ENVIRONMENT)) {
// Pushing another Env Instruction would be redundant so only Marker needs to be pushed.
agenda.push(instr.markerInstr())
agenda.push(instr.markerInstr(command.srcNode))
} else if (!isNode(next) && next.instrType === InstrType.RESET) {
// Reset Instruction will be replaced by Reset Instruction of new return statement.
agenda.pop()
} else {
agenda.push(instr.envInstr(currentEnvironment(context)))
agenda.push(instr.markerInstr())
agenda.push(instr.envInstr(currentEnvironment(context), command.srcNode))
agenda.push(instr.markerInstr(command.srcNode))
}
// Create environment for function parameters if the function isn't nullary.
// Name the environment if the function call expression is not anonymous
Expand Down Expand Up @@ -835,7 +846,7 @@
stash.push(value)
},

[InstrType.CONTINUE]: function (command: Instr, context: Context, agenda: Agenda, stash: Stash) {

Check warning on line 849 in src/ec-evaluator/interpreter.ts

View workflow job for this annotation

GitHub Actions / build (16.x)

'stash' is defined but never used. Allowed unused args must match /^_/u
const next = agenda.pop() as AgendaItem
if (isInstr(next) && next.instrType == InstrType.CONTINUE_MARKER) {
// Encountered continue mark, stop popping
Expand All @@ -850,7 +861,7 @@

[InstrType.CONTINUE_MARKER]: function () {},

[InstrType.BREAK]: function (command: Instr, context: Context, agenda: Agenda, stash: Stash) {

Check warning on line 864 in src/ec-evaluator/interpreter.ts

View workflow job for this annotation

GitHub Actions / build (16.x)

'stash' is defined but never used. Allowed unused args must match /^_/u
const next = agenda.pop() as AgendaItem
if (isInstr(next) && next.instrType == InstrType.BREAK_MARKER) {
// Encountered break mark, stop popping
Expand Down
Loading
Loading