Skip to content

Commit

Permalink
Implement statement sequence for the CSE machine (#1575)
Browse files Browse the repository at this point in the history
* Creation of StatementSequence, custom Node type and changes in CSE machine

* fix formatting

* Implement AST transformer and  transformation

* formatting

* bug fix #1

* limit places where transformed tree is run

* Sent statementSequence transformer to utils + transform happens immediately before execution

* format

* remove debug

* i am actually so stupid

* Completion of CSE machine handling of StatementSequence

* Clousre creation now accounts for StatementSequence

* hasBreak/Continue/Return now accounts for StatementSequence

* astToString now works on StatementSequence

* wait this should not be here

* Add Program to StatementSequence transformer + update CSE machine implementation

* minor suggestions from code review

---------

Co-authored-by: Martin Henz <[email protected]>
  • Loading branch information
NhatMinh0208 and martin-henz authored Mar 20, 2024
1 parent 746eb8b commit 00f3153
Show file tree
Hide file tree
Showing 61 changed files with 938 additions and 410 deletions.
44 changes: 20 additions & 24 deletions src/cse-machine/instrCreator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import * as es from 'estree'

import { Environment } from '../types'
import { Environment, Node } from '../types'
import {
AppInstr,
ArrLitInstr,
Expand All @@ -21,16 +21,12 @@ import {
WhileInstr
} from './types'

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

export const whileInstr = (
test: es.Expression,
body: es.Statement,
srcNode: es.Node
): WhileInstr => ({
export const whileInstr = (test: es.Expression, body: es.Statement, srcNode: Node): WhileInstr => ({
instrType: InstrType.WHILE,
test,
body,
Expand All @@ -42,7 +38,7 @@ export const forInstr = (
test: es.Expression,
update: es.Expression,
body: es.Statement,
srcNode: es.Node
srcNode: Node
): ForInstr => ({
instrType: InstrType.FOR,
init,
Expand All @@ -56,7 +52,7 @@ export const assmtInstr = (
symbol: string,
constant: boolean,
declaration: boolean,
srcNode: es.Node
srcNode: Node
): AssmtInstr => ({
instrType: InstrType.ASSIGNMENT,
symbol,
Expand All @@ -65,19 +61,19 @@ export const assmtInstr = (
srcNode
})

export const unOpInstr = (symbol: es.UnaryOperator, srcNode: es.Node): UnOpInstr => ({
export const unOpInstr = (symbol: es.UnaryOperator, srcNode: Node): UnOpInstr => ({
instrType: InstrType.UNARY_OP,
symbol,
srcNode
})

export const binOpInstr = (symbol: es.BinaryOperator, srcNode: es.Node): BinOpInstr => ({
export const binOpInstr = (symbol: es.BinaryOperator, srcNode: Node): BinOpInstr => ({
instrType: InstrType.BINARY_OP,
symbol,
srcNode
})

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

export const appInstr = (numOfArgs: number, srcNode: es.CallExpression): AppInstr => ({
instrType: InstrType.APPLICATION,
Expand All @@ -88,67 +84,67 @@ export const appInstr = (numOfArgs: number, srcNode: es.CallExpression): AppInst
export const branchInstr = (
consequent: es.Expression | es.Statement,
alternate: es.Expression | es.Statement | null | undefined,
srcNode: es.Node
srcNode: Node
): BranchInstr => ({
instrType: InstrType.BRANCH,
consequent,
alternate,
srcNode
})

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

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

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

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

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

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

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

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

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

export const genContInstr = (srcNode: es.Node): GenContInstr => ({
export const genContInstr = (srcNode: Node): GenContInstr => ({
instrType: InstrType.GENERATE_CONT,
srcNode
})

export const resumeContInstr = (srcNode: es.Node): ResumeContInstr => ({
export const resumeContInstr = (srcNode: Node): ResumeContInstr => ({
instrType: InstrType.RESUME_CONT,
srcNode
})
84 changes: 42 additions & 42 deletions src/cse-machine/interpreter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@ import { initModuleContext, loadModuleBundle } from '../modules/moduleLoader'
import { ImportTransformOptions } from '../modules/moduleTypes'
import { checkEditorBreakpoints } from '../stdlib/inspector'
import { checkProgramForUndefinedVariables } from '../transpiler/transpiler'
import { Context, ContiguousArrayElements, RawBlockStatement, Result, Value } from '../types'
import { Context, ContiguousArrayElements, Result, StatementSequence, Value } from '../types'
import assert from '../utils/assert'
import { filterImportDeclarations } from '../utils/ast/helpers'
import * as ast from '../utils/astCreator'
import { evaluateBinaryExpression, evaluateUnaryExpression } from '../utils/operators'
import * as rttc from '../utils/rttc'
import * as seq from '../utils/statementSeqTransform'
import {
Continuation,
getContinuationControl,
Expand Down Expand Up @@ -72,7 +73,6 @@ import {
isBlockStatement,
isInstr,
isNode,
isRawBlockStatement,
isSimpleFunction,
popEnvironment,
pushEnvironment,
Expand All @@ -95,7 +95,7 @@ type CmdEvaluator = (
* It contains syntax tree nodes or instructions.
*/
export class Control extends Stack<ControlItem> {
public constructor(program?: es.Program) {
public constructor(program?: es.Program | StatementSequence) {
super()

// Load program into control stack
Expand All @@ -109,20 +109,18 @@ export class Control extends Stack<ControlItem> {

/**
* Before pushing block statements on the control stack, we check if the block statement has any declarations.
* If not (and its not a raw block statement), instead of pushing the entire block, just the body is pushed since the block is not adding any value.
* If not, the block is converted to a StatementSequence.
* @param items The items being pushed on the control.
* @returns The same set of control items, but with block statements without declarations simplified.
* @returns The same set of control items, but with block statements without declarations converted to StatementSequences.
* NOTE: this function handles any case where StatementSequence has to be converted back into BlockStatement due to type issues
*/
private static simplifyBlocksWithoutDeclarations(...items: ControlItem[]): ControlItem[] {
const itemsNew: ControlItem[] = []
items.forEach(item => {
if (
isNode(item) &&
isBlockStatement(item) &&
!hasDeclarations(item) &&
!isRawBlockStatement(item)
) {
itemsNew.push(...Control.simplifyBlocksWithoutDeclarations(...handleSequence(item.body)))
if (isNode(item) && isBlockStatement(item) && !hasDeclarations(item)) {
// Push block body as statement sequence
const seq: StatementSequence = ast.statementSequence(item.body, item.loc)
itemsNew.push(seq)
} else {
itemsNew.push(item)
}
Expand Down Expand Up @@ -169,6 +167,7 @@ export function evaluate(program: es.Program, context: Context, options: IOption
context.errors.push(error)
return new CseError(error)
}
seq.transform(program)

try {
context.runtime.isRunning = true
Expand Down Expand Up @@ -311,8 +310,11 @@ export function* generateCSEMachineStateStream(

let command = control.peek()

// First node will be a Program
context.runtime.nodes.unshift(command as es.Program)
// Push first node to be evaluated into context.
// The typeguard is there to guarantee that we are pushing a node (which should always be the case)
if (command && isNode(command)) {
context.runtime.nodes.unshift(command)
}

while (command) {
// Return to capture a snapshot of the control and stash after the target step count is reached
Expand Down Expand Up @@ -369,7 +371,7 @@ export function* generateCSEMachineStateStream(

/**
* Dictionary of functions which handle the logic for the response of the three registers of
* the ASE machine to each ControlItem.
* the CSE machine to each ControlItem.
*/
const cmdEvaluators: { [type: string]: CmdEvaluator } = {
/**
Expand Down Expand Up @@ -400,31 +402,17 @@ const cmdEvaluators: { [type: string]: CmdEvaluator } = {
const next = command.body[0]
cmdEvaluators[next.type](next, context, control, stash, isPrelude)
} else {
// Push raw block statement
const rawCopy: RawBlockStatement = {
type: 'BlockStatement',
range: command.range,
loc: command.loc,
body: command.body,
isRawBlock: 'true'
}
control.push(rawCopy)
// Push block body as statement sequence
const seq: StatementSequence = ast.statementSequence(command.body, command.loc)
control.push(seq)
}
},

BlockStatement: function (command: es.BlockStatement, context: Context, control: Control) {
if (isRawBlockStatement(command)) {
// Raw block statement: unpack and push body
// Push block body only
control.push(...handleSequence(command.body))
return
}
// Normal block statement: do environment setup
// To restore environment after block ends
// If there is an env instruction on top of the stack, or if there are no declarations
// we do not need to push another one
// The no declarations case is handled by Control :: simplifyBlocksWithoutDeclarations, so no blockStatement node
// without declarations should end up here.
// The no declarations case is handled at the transform stage, so no blockStatement node without declarations should end up here.
const next = control.peek()
// Push ENVIRONMENT instruction if needed
if (!next || !(isInstr(next) && next.instrType === InstrType.ENVIRONMENT)) {
Expand All @@ -435,15 +423,27 @@ const cmdEvaluators: { [type: string]: CmdEvaluator } = {
declareFunctionsAndVariables(context, command, environment)
pushEnvironment(context, environment)

// Push raw block statement
const rawCopy: RawBlockStatement = {
type: 'BlockStatement',
range: command.range,
loc: command.loc,
body: command.body,
isRawBlock: 'true'
// Push block body as statement sequence
const seq: StatementSequence = ast.statementSequence(command.body, command.loc)
control.push(seq)
},

StatementSequence: function (
command: StatementSequence,
context: Context,
control: Control,
stash: Stash,
isPrelude: boolean
) {
if (command.body.length == 1) {
// If sequence only consists of one statement, evaluate it immediately
const next = command.body[0]
cmdEvaluators[next.type](next, context, control, stash, isPrelude)
} else {
// unpack and push individual nodes in body
control.push(...handleSequence(command.body))
}
control.push(rawCopy)
return
},

WhileStatement: function (
Expand Down Expand Up @@ -981,7 +981,7 @@ const cmdEvaluators: { [type: string]: CmdEvaluator } = {
// Value is a function
// Check for number of arguments mismatch error
checkNumberOfArguments(context, func, args, command.srcNode)
// Directly stash result of applying pre-built functions without the ASE machine.
// Directly stash result of applying pre-built functions without the CSE machine.
try {
const result = func(...args)
stash.push(result)
Expand Down
6 changes: 3 additions & 3 deletions src/cse-machine/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as es from 'estree'

import { Environment } from '../types'
import { Environment, Node } from '../types'

export enum InstrType {
RESET = 'Reset',
Expand Down Expand Up @@ -28,7 +28,7 @@ export enum InstrType {

interface BaseInstr {
instrType: InstrType
srcNode: es.Node
srcNode: Node
}

export interface WhileInstr extends BaseInstr {
Expand Down Expand Up @@ -90,7 +90,7 @@ export type Instr =
| GenContInstr
| ResumeContInstr

export type ControlItem = es.Node | Instr
export type ControlItem = Node | Instr

// Special class that cannot be found on the stash so is safe to be used
// as an indicator of a breakpoint from running the CSE machine
Expand Down
Loading

0 comments on commit 00f3153

Please sign in to comment.