From 00f3153ff3aed629d18dfb0c21c9e1128913f1c2 Mon Sep 17 00:00:00 2001 From: Theoth Normie Date: Wed, 20 Mar 2024 11:24:24 +0800 Subject: [PATCH] Implement statement sequence for the CSE machine (#1575) * 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 --- src/cse-machine/instrCreator.ts | 44 +- src/cse-machine/interpreter.ts | 84 ++-- src/cse-machine/types.ts | 6 +- src/cse-machine/utils.ts | 94 ++-- src/errors/errors.ts | 24 +- src/errors/moduleErrors.ts | 9 +- src/errors/runtimeSourceError.ts | 4 +- src/errors/timeoutErrors.ts | 8 +- src/errors/typeErrors.ts | 15 +- src/finder.ts | 3 +- src/gpu/transfomer.ts | 3 +- src/infiniteLoops/instrument.ts | 15 +- src/interpreter/closure.ts | 27 +- src/interpreter/interpreter-non-det.ts | 16 +- src/interpreter/interpreter.ts | 28 +- .../transformers/removeExports.ts | 13 +- .../removeNonSourceModuleImports.ts | 15 +- src/modules/errors.ts | 7 +- src/modules/moduleLoader.ts | 11 +- src/name-extractor/index.ts | 29 +- src/parser/errors.ts | 3 +- src/parser/source/index.ts | 26 +- src/parser/source/rules/bracesAroundFor.ts | 4 +- src/parser/source/rules/bracesAroundIfElse.ts | 4 +- src/parser/source/rules/bracesAroundWhile.ts | 4 +- src/parser/source/rules/index.ts | 6 +- src/parser/source/rules/noDeclareMutable.ts | 4 +- src/parser/source/rules/noDotAbbreviation.ts | 4 +- src/parser/source/rules/noEval.ts | 4 +- .../noExportNamedDeclarationWithDefault.ts | 4 +- .../noFunctionDeclarationWithoutIdentifier.ts | 4 +- src/parser/source/rules/noIfWithoutElse.ts | 4 +- .../rules/noImplicitDeclareUndefined.ts | 4 +- .../source/rules/noImplicitReturnUndefined.ts | 4 +- .../rules/noImportSpecifierWithDefault.ts | 4 +- src/parser/source/rules/noNull.ts | 4 +- src/parser/source/rules/noSpreadInArray.ts | 4 +- .../source/rules/noTemplateExpression.ts | 4 +- .../source/rules/noUnspecifiedLiteral.ts | 4 +- .../source/rules/noUnspecifiedOperator.ts | 4 +- src/parser/source/rules/noUpdateAssignment.ts | 4 +- src/parser/source/rules/noVar.ts | 4 +- .../source/rules/singleVariableDeclaration.ts | 4 +- src/parser/source/rules/strictEquality.ts | 4 +- src/runner/sourceRunner.ts | 4 +- src/scope-refactoring.ts | 4 +- src/stdlib/inspector.ts | 4 +- src/stdlib/parser.ts | 23 +- src/stepper/util.ts | 4 +- src/transpiler/transpiler.ts | 35 +- src/typeChecker/internalTypeErrors.ts | 5 +- src/types.ts | 35 +- src/utils/ast/typeGuards.ts | 8 +- src/utils/astCreator.ts | 26 +- src/utils/astToString.ts | 86 +++- src/utils/dummyAstCreator.ts | 2 +- src/utils/rttc.ts | 16 +- src/utils/statementSeqTransform.ts | 435 ++++++++++++++++++ src/utils/walkers.ts | 3 +- src/validator/validator.ts | 28 +- src/vm/svml-compiler.ts | 56 +-- 61 files changed, 938 insertions(+), 410 deletions(-) create mode 100644 src/utils/statementSeqTransform.ts diff --git a/src/cse-machine/instrCreator.ts b/src/cse-machine/instrCreator.ts index c0974ff2d..731708461 100644 --- a/src/cse-machine/instrCreator.ts +++ b/src/cse-machine/instrCreator.ts @@ -4,7 +4,7 @@ import * as es from 'estree' -import { Environment } from '../types' +import { Environment, Node } from '../types' import { AppInstr, ArrLitInstr, @@ -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, @@ -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, @@ -56,7 +52,7 @@ export const assmtInstr = ( symbol: string, constant: boolean, declaration: boolean, - srcNode: es.Node + srcNode: Node ): AssmtInstr => ({ instrType: InstrType.ASSIGNMENT, symbol, @@ -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, @@ -88,7 +84,7 @@ 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, @@ -96,59 +92,59 @@ export const branchInstr = ( 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 }) diff --git a/src/cse-machine/interpreter.ts b/src/cse-machine/interpreter.ts index 426e12605..d1274de7d 100644 --- a/src/cse-machine/interpreter.ts +++ b/src/cse-machine/interpreter.ts @@ -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, @@ -72,7 +73,6 @@ import { isBlockStatement, isInstr, isNode, - isRawBlockStatement, isSimpleFunction, popEnvironment, pushEnvironment, @@ -95,7 +95,7 @@ type CmdEvaluator = ( * It contains syntax tree nodes or instructions. */ export class Control extends Stack { - public constructor(program?: es.Program) { + public constructor(program?: es.Program | StatementSequence) { super() // Load program into control stack @@ -109,20 +109,18 @@ export class Control extends Stack { /** * 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) } @@ -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 @@ -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 @@ -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 } = { /** @@ -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)) { @@ -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 ( @@ -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) diff --git a/src/cse-machine/types.ts b/src/cse-machine/types.ts index e6efe9290..0239762a0 100644 --- a/src/cse-machine/types.ts +++ b/src/cse-machine/types.ts @@ -1,6 +1,6 @@ import * as es from 'estree' -import { Environment } from '../types' +import { Environment, Node } from '../types' export enum InstrType { RESET = 'Reset', @@ -28,7 +28,7 @@ export enum InstrType { interface BaseInstr { instrType: InstrType - srcNode: es.Node + srcNode: Node } export interface WhileInstr extends BaseInstr { @@ -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 diff --git a/src/cse-machine/utils.ts b/src/cse-machine/utils.ts index 8dbaf7f90..6efbe1393 100644 --- a/src/cse-machine/utils.ts +++ b/src/cse-machine/utils.ts @@ -5,7 +5,7 @@ import { Context } from '..' import * as errors from '../errors/errors' import { RuntimeSourceError } from '../errors/runtimeSourceError' import Closure from '../interpreter/closure' -import { Environment, Frame, RawBlockStatement, Value } from '../types' +import { Environment, Frame, Node, StatementSequence, Value } from '../types' import * as ast from '../utils/astCreator' import * as instr from './instrCreator' import { Control } from './interpreter' @@ -81,72 +81,72 @@ export const isInstr = (command: ControlItem): command is Instr => { } /** - * Typeguard for esNode to distinguish between program statements and instructions. + * Typeguard for Node to distinguish between program statements and instructions. * * @param command A ControlItem - * @returns true if the ControlItem is an esNode and false if it is an instruction. + * @returns true if the ControlItem is a Node or StatementSequence, false if it is an instruction. */ -export const isNode = (command: ControlItem): command is es.Node => { - return (command as es.Node).type !== undefined +export const isNode = (command: ControlItem): command is Node => { + return (command as Node).type !== undefined } /** - * Typeguard for esIdentifier. To verify if an esNode is an esIdentifier. + * Typeguard for esIdentifier. To verify if a Node is an esIdentifier. * - * @param node an esNode + * @param node a Node * @returns true if node is an esIdentifier, false otherwise. */ -export const isIdentifier = (node: es.Node): node is es.Identifier => { +export const isIdentifier = (node: Node): node is es.Identifier => { return (node as es.Identifier).name !== undefined } /** - * Typeguard for esReturnStatement. To verify if an esNode is an esReturnStatement. + * Typeguard for esReturnStatement. To verify if a Node is an esReturnStatement. * - * @param node an esNode + * @param node a Node * @returns true if node is an esReturnStatement, false otherwise. */ -export const isReturnStatement = (node: es.Node): node is es.ReturnStatement => { +export const isReturnStatement = (node: Node): node is es.ReturnStatement => { return (node as es.ReturnStatement).type == 'ReturnStatement' } /** - * Typeguard for esIfStatement. To verify if an esNode is an esIfStatement. + * Typeguard for esIfStatement. To verify if a Node is an esIfStatement. * - * @param node an esNode + * @param node a Node * @returns true if node is an esIfStatement, false otherwise. */ -export const isIfStatement = (node: es.Node): node is es.IfStatement => { +export const isIfStatement = (node: Node): node is es.IfStatement => { return (node as es.IfStatement).type == 'IfStatement' } /** - * Typeguard for esBlockStatement. To verify if an esNode is a block statement. + * Typeguard for esBlockStatement. To verify if a Node is a block statement. * - * @param node an esNode + * @param node a Node * @returns true if node is an esBlockStatement, false otherwise. */ -export const isBlockStatement = (node: es.Node): node is es.BlockStatement => { +export const isBlockStatement = (node: Node): node is es.BlockStatement => { return (node as es.BlockStatement).type == 'BlockStatement' } /** - * Typeguard for RawBlockStatement. To verify if an esNode is a raw block statement (i.e. passed environment creation). + * Typeguard for StatementSequence. To verify if a ControlItem is a statement sequence. * - * @param node an esNode - * @returns true if node is a RawBlockStatement, false otherwise. + * @param node a ControlItem + * @returns true if node is a StatementSequence, false otherwise. */ -export const isRawBlockStatement = (node: es.Node): node is RawBlockStatement => { - return (node as RawBlockStatement).isRawBlock === 'true' +export const isStatementSequence = (node: ControlItem): node is StatementSequence => { + return (node as StatementSequence).type == 'StatementSequence' } /** - * Typeguard for esRestElement. To verify if an esNode is a block statement. + * Typeguard for esRestElement. To verify if a Node is a block statement. * - * @param node an esNode + * @param node a Node * @returns true if node is an esRestElement, false otherwise. */ -export const isRestElement = (node: es.Node): node is es.RestElement => { +export const isRestElement = (node: Node): node is es.RestElement => { return (node as es.RestElement).type == 'RestElement' } @@ -207,7 +207,7 @@ export const reduceConditional = ( * @param command Control item to determine if it is value producing. * @returns true if it is value producing, false otherwise. */ -export const valueProducing = (command: es.Node): boolean => { +export const valueProducing = (command: Node): boolean => { const type = command.type return ( type !== 'VariableDeclaration' && @@ -233,7 +233,7 @@ export const valueProducing = (command: es.Node): boolean => { export const envChanging = (command: ControlItem): boolean => { if (isNode(command)) { const type = command.type - return type === 'Program' || (type === 'BlockStatement' && hasDeclarations(command)) + return type === 'Program' || type === 'BlockStatement' } else { const type = command.instrType return ( @@ -251,8 +251,8 @@ export const envChanging = (command: ControlItem): boolean => { * @param node The function to check against. * @returns true if the function is simple, false otherwise. */ -export const isSimpleFunction = (node: es.Function) => { - if (node.body.type !== 'BlockStatement') { +export const isSimpleFunction = (node: any) => { + if (node.body.type !== 'BlockStatement' && node.body.type !== 'StatementSequence') { return true } else { const block = node.body @@ -321,7 +321,7 @@ const UNASSIGNED_LET = Symbol('let declaration') export function declareIdentifier( context: Context, name: string, - node: es.Node, + node: Node, environment: Environment, constant: boolean = false ) { @@ -391,7 +391,7 @@ export function hasImportDeclarations(node: es.BlockStatement): boolean { return false } -function isImportDeclaration(node: es.Node): boolean { +function isImportDeclaration(node: Node): boolean { return node.type === 'ImportDeclaration' } @@ -548,8 +548,8 @@ export const hasReturnStatementIf = (statement: es.IfStatement): boolean => { if (statement.alternate) { if (isIfStatement(statement.alternate)) { hasReturn = hasReturn && hasReturnStatementIf(statement.alternate as es.IfStatement) - } else if (isBlockStatement(statement.alternate)) { - hasReturn = hasReturn && hasReturnStatement(statement.alternate as es.BlockStatement) + } else if (isBlockStatement(statement.alternate) || isStatementSequence(statement.alternate)) { + hasReturn = hasReturn && hasReturnStatement(statement.alternate) } } return hasReturn @@ -560,7 +560,7 @@ export const hasReturnStatementIf = (statement: es.IfStatement): boolean => { * @param body The block to be checked * @return `true` if every branch has a return statement, else `false`. */ -export const hasReturnStatement = (block: es.BlockStatement): boolean => { +export const hasReturnStatement = (block: es.BlockStatement | StatementSequence): boolean => { let hasReturn = false for (const statement of block.body) { if (isReturnStatement(statement)) { @@ -568,6 +568,8 @@ export const hasReturnStatement = (block: es.BlockStatement): boolean => { } else if (isIfStatement(statement)) { // Parser enforces that if/else have braces (block statement) hasReturn = hasReturn || hasReturnStatementIf(statement as es.IfStatement) + } else if (isBlockStatement(statement) || isStatementSequence(statement)) { + hasReturn = hasReturn && hasReturnStatement(statement) } } return hasReturn @@ -579,9 +581,9 @@ export const hasBreakStatementIf = (statement: es.IfStatement): boolean => { hasBreak = hasBreak || hasBreakStatement(statement.consequent as es.BlockStatement) if (statement.alternate) { if (isIfStatement(statement.alternate)) { - hasBreak = hasBreak || hasBreakStatementIf(statement.alternate as es.IfStatement) - } else if (isBlockStatement(statement.alternate)) { - hasBreak = hasBreak || hasBreakStatement(statement.alternate as es.BlockStatement) + hasBreak = hasBreak || hasBreakStatementIf(statement.alternate) + } else if (isBlockStatement(statement.alternate) || isStatementSequence(statement.alternate)) { + hasBreak = hasBreak || hasBreakStatement(statement.alternate) } } return hasBreak @@ -592,7 +594,7 @@ export const hasBreakStatementIf = (statement: es.IfStatement): boolean => { * @param body The block to be checked * @return `true` if there is a `break` statement, else `false`. */ -export const hasBreakStatement = (block: es.BlockStatement): boolean => { +export const hasBreakStatement = (block: es.BlockStatement | StatementSequence): boolean => { let hasBreak = false for (const statement of block.body) { if (statement.type === 'BreakStatement') { @@ -600,8 +602,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) + } else if (isBlockStatement(statement) || isStatementSequence(statement)) { + hasBreak = hasBreak || hasBreakStatement(statement) } } return hasBreak @@ -613,9 +615,9 @@ export const hasContinueStatementIf = (statement: es.IfStatement): boolean => { hasContinue = hasContinue || hasContinueStatement(statement.consequent as es.BlockStatement) if (statement.alternate) { if (isIfStatement(statement.alternate)) { - hasContinue = hasContinue || hasContinueStatementIf(statement.alternate as es.IfStatement) - } else if (isBlockStatement(statement.alternate)) { - hasContinue = hasContinue || hasContinueStatement(statement.alternate as es.BlockStatement) + hasContinue = hasContinue || hasContinueStatementIf(statement.alternate) + } else if (isBlockStatement(statement.alternate) || isStatementSequence(statement.alternate)) { + hasContinue = hasContinue || hasContinueStatement(statement.alternate) } } return hasContinue @@ -626,7 +628,7 @@ export const hasContinueStatementIf = (statement: es.IfStatement): boolean => { * @param body The block to be checked * @return `true` if there is a `continue` statement, else `false`. */ -export const hasContinueStatement = (block: es.BlockStatement): boolean => { +export const hasContinueStatement = (block: es.BlockStatement | StatementSequence): boolean => { let hasContinue = false for (const statement of block.body) { if (statement.type === 'ContinueStatement') { @@ -634,8 +636,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) + } else if (isBlockStatement(statement) || isStatementSequence(statement)) { + hasContinue = hasContinue || hasContinueStatement(statement) } } return hasContinue diff --git a/src/errors/errors.ts b/src/errors/errors.ts index fadb48847..1cade0493 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -4,12 +4,12 @@ import { baseGenerator, generate } from 'astring' import * as es from 'estree' import { UNKNOWN_LOCATION } from '../constants' -import { ErrorSeverity, ErrorType, SourceError, Value } from '../types' +import { ErrorSeverity, ErrorType, Node, SourceError, Value } from '../types' import { stringify } from '../utils/stringify' import { RuntimeSourceError } from './runtimeSourceError' export class InterruptedError extends RuntimeSourceError { - constructor(node: es.Node) { + constructor(node: Node) { super(node) } @@ -54,7 +54,7 @@ export class MaximumStackLimitExceeded extends RuntimeSourceError { } } - constructor(node: es.Node, private calls: es.CallExpression[]) { + constructor(node: Node, private calls: es.CallExpression[]) { super(node) } @@ -71,7 +71,7 @@ export class MaximumStackLimitExceeded extends RuntimeSourceError { } export class CallingNonFunctionValue extends RuntimeSourceError { - constructor(private callee: Value, private node: es.Node) { + constructor(private callee: Value, private node: Node) { super(node) } @@ -100,7 +100,7 @@ export class CallingNonFunctionValue extends RuntimeSourceError { } export class UndefinedVariable extends RuntimeSourceError { - constructor(public name: string, node: es.Node) { + constructor(public name: string, node: Node) { super(node) } @@ -114,7 +114,7 @@ export class UndefinedVariable extends RuntimeSourceError { } export class UnassignedVariable extends RuntimeSourceError { - constructor(public name: string, node: es.Node) { + constructor(public name: string, node: Node) { super(node) } @@ -131,7 +131,7 @@ export class InvalidNumberOfArguments extends RuntimeSourceError { private calleeStr: string constructor( - node: es.Node, + node: Node, private expected: number, private got: number, private hasVarArgs = false @@ -155,7 +155,7 @@ export class InvalidNumberOfArguments extends RuntimeSourceError { } export class VariableRedeclaration extends RuntimeSourceError { - constructor(private node: es.Node, private name: string, private writable?: boolean) { + constructor(private node: Node, private name: string, private writable?: boolean) { super(node) } @@ -186,7 +186,7 @@ export class VariableRedeclaration extends RuntimeSourceError { } export class ConstAssignment extends RuntimeSourceError { - constructor(node: es.Node, private name: string) { + constructor(node: Node, private name: string) { super(node) } @@ -200,7 +200,7 @@ export class ConstAssignment extends RuntimeSourceError { } export class GetPropertyError extends RuntimeSourceError { - constructor(node: es.Node, private obj: Value, private prop: string) { + constructor(node: Node, private obj: Value, private prop: string) { super(node) } @@ -218,7 +218,7 @@ export class GetInheritedPropertyError extends RuntimeSourceError { public severity = ErrorSeverity.ERROR public location: es.SourceLocation - constructor(node: es.Node, private obj: Value, private prop: string) { + constructor(node: Node, private obj: Value, private prop: string) { super(node) this.location = node.loc ?? UNKNOWN_LOCATION } @@ -233,7 +233,7 @@ export class GetInheritedPropertyError extends RuntimeSourceError { } export class SetPropertyError extends RuntimeSourceError { - constructor(node: es.Node, private obj: Value, private prop: string) { + constructor(node: Node, private obj: Value, private prop: string) { super(node) } diff --git a/src/errors/moduleErrors.ts b/src/errors/moduleErrors.ts index 683d6089a..94214d4ee 100644 --- a/src/errors/moduleErrors.ts +++ b/src/errors/moduleErrors.ts @@ -1,12 +1,11 @@ /* tslint:disable: max-classes-per-file */ -import * as es from 'estree' - +import { Node } from '../types' import { RuntimeSourceError } from './runtimeSourceError' export class ModuleConnectionError extends RuntimeSourceError { private static message: string = `Unable to get modules.` private static elaboration: string = `You should check your Internet connection, and ensure you have used the correct module path.` - constructor(node?: es.Node) { + constructor(node?: Node) { super(node) } @@ -20,7 +19,7 @@ export class ModuleConnectionError extends RuntimeSourceError { } export class ModuleNotFoundError extends RuntimeSourceError { - constructor(public moduleName: string, node?: es.Node) { + constructor(public moduleName: string, node?: Node) { super(node) } @@ -36,7 +35,7 @@ export class ModuleNotFoundError extends RuntimeSourceError { } export class ModuleInternalError extends RuntimeSourceError { - constructor(public moduleName: string, public error?: any, node?: es.Node) { + constructor(public moduleName: string, public error?: any, node?: Node) { super(node) } diff --git a/src/errors/runtimeSourceError.ts b/src/errors/runtimeSourceError.ts index b72907b07..9bccc77f1 100644 --- a/src/errors/runtimeSourceError.ts +++ b/src/errors/runtimeSourceError.ts @@ -1,14 +1,14 @@ import * as es from 'estree' import { UNKNOWN_LOCATION } from '../constants' -import { ErrorSeverity, ErrorType, SourceError } from '../types' +import { ErrorSeverity, ErrorType, Node, SourceError } from '../types' export class RuntimeSourceError implements SourceError { public type = ErrorType.RUNTIME public severity = ErrorSeverity.ERROR public location: es.SourceLocation - constructor(node?: es.Node) { + constructor(node?: Node) { this.location = node?.loc ?? UNKNOWN_LOCATION } diff --git a/src/errors/timeoutErrors.ts b/src/errors/timeoutErrors.ts index 461a5dd97..b0e46719d 100644 --- a/src/errors/timeoutErrors.ts +++ b/src/errors/timeoutErrors.ts @@ -1,8 +1,6 @@ /* tslint:disable:max-classes-per-file */ -import * as es from 'estree' - import { JSSLANG_PROPERTIES } from '../constants' -import { ErrorSeverity, ErrorType } from '../types' +import { ErrorSeverity, ErrorType, Node } from '../types' import { stripIndent } from '../utils/formatters' import { stringify } from '../utils/stringify' import { RuntimeSourceError } from './runtimeSourceError' @@ -21,7 +19,7 @@ export class PotentialInfiniteLoopError extends TimeoutError { public type = ErrorType.RUNTIME public severity = ErrorSeverity.ERROR - constructor(node: es.Node, private maxExecTime: number) { + constructor(node: Node, private maxExecTime: number) { super(node) } @@ -39,7 +37,7 @@ export class PotentialInfiniteRecursionError extends TimeoutError { public type = ErrorType.RUNTIME public severity = ErrorSeverity.ERROR - constructor(node: es.Node, private calls: [string, any[]][], private maxExecTime: number) { + constructor(node: Node, private calls: [string, any[]][], private maxExecTime: number) { super(node) this.calls = this.calls.slice(-3) } diff --git a/src/errors/typeErrors.ts b/src/errors/typeErrors.ts index b32f36710..eac30472a 100644 --- a/src/errors/typeErrors.ts +++ b/src/errors/typeErrors.ts @@ -4,6 +4,7 @@ import * as es from 'estree' import { UNKNOWN_LOCATION } from '../constants' import * as tsEs from '../typeChecker/tsESTree' import { ErrorSeverity, ErrorType, NodeWithInferredType, SArray, SourceError, Type } from '../types' +import { Node } from '../types' import { simplify, stripIndent } from '../utils/formatters' import { typeToString } from '../utils/stringify' @@ -13,7 +14,7 @@ export class InvalidArrayIndexType implements SourceError { public type = ErrorType.TYPE public severity = ErrorSeverity.WARNING - constructor(public node: NodeWithInferredType, public receivedType: Type) {} + constructor(public node: NodeWithInferredType, public receivedType: Type) {} get location() { return this.node.loc ?? UNKNOWN_LOCATION @@ -33,7 +34,7 @@ export class ArrayAssignmentError implements SourceError { public severity = ErrorSeverity.WARNING constructor( - public node: NodeWithInferredType, + public node: NodeWithInferredType, public arrayType: SArray, public receivedType: SArray ) {} @@ -113,7 +114,7 @@ export class CyclicReferenceError implements SourceError { public type = ErrorType.TYPE public severity = ErrorSeverity.WARNING - constructor(public node: NodeWithInferredType) {} + constructor(public node: NodeWithInferredType) {} get location() { return this.node.loc ?? UNKNOWN_LOCATION @@ -128,7 +129,7 @@ export class CyclicReferenceError implements SourceError { } } -function stringifyNode(node: NodeWithInferredType): string { +function stringifyNode(node: NodeWithInferredType): string { return ['VariableDeclaration', 'FunctionDeclaration'].includes(node.type) ? node.type === 'VariableDeclaration' ? (node.declarations[0].id as es.Identifier).name @@ -143,7 +144,7 @@ export class DifferentNumberArgumentsError implements SourceError { public severity = ErrorSeverity.WARNING constructor( - public node: NodeWithInferredType, + public node: NodeWithInferredType, public numExpectedArgs: number, public numReceived: number ) {} @@ -165,8 +166,8 @@ export class InvalidArgumentTypesError implements SourceError { public severity = ErrorSeverity.WARNING constructor( - public node: NodeWithInferredType, - public args: NodeWithInferredType[], + public node: NodeWithInferredType, + public args: NodeWithInferredType[], public expectedTypes: Type[], public receivedTypes: Type[] ) {} diff --git a/src/finder.ts b/src/finder.ts index 87b56cab8..4a3b9b6fc 100644 --- a/src/finder.ts +++ b/src/finder.ts @@ -5,12 +5,11 @@ import { FunctionDeclaration, Identifier, ImportSpecifier, - Node, SourceLocation, VariableDeclarator } from 'estree' -import { Context } from './types' +import { Context, Node } from './types' import { ancestor, base, diff --git a/src/gpu/transfomer.ts b/src/gpu/transfomer.ts index 8a9d39d2d..15fbfe540 100644 --- a/src/gpu/transfomer.ts +++ b/src/gpu/transfomer.ts @@ -1,5 +1,6 @@ import * as es from 'estree' +import { Node } from '../types' import * as create from '../utils/astCreator' import { ancestor, make, simple } from '../utils/walkers' import GPUBodyVerifier from './verification/bodyVerifier' @@ -114,7 +115,7 @@ class GPUTransformer { const checker = verifier.getArrayName const locals = this.localVar ancestor(this.targetBody, { - AssignmentExpression(nx: es.AssignmentExpression, ancstor: es.Node[]) { + AssignmentExpression(nx: es.AssignmentExpression, ancstor: Node[]) { // assigning to local val, it's okay if (nx.left.type === 'Identifier') { return diff --git a/src/infiniteLoops/instrument.ts b/src/infiniteLoops/instrument.ts index 6b3af9749..562f66be3 100644 --- a/src/infiniteLoops/instrument.ts +++ b/src/infiniteLoops/instrument.ts @@ -2,6 +2,7 @@ import { generate } from 'astring' import * as es from 'estree' import { transformImportDeclarations } from '../transpiler/transpiler' +import { Node } from '../types' import * as create from '../utils/astCreator' import { recursive, simple, WalkerCallback } from '../utils/walkers' // transforms AST of program @@ -37,7 +38,7 @@ enum FunctionNames { * E.g. "function f(f)..." -> "function f_0(f_1)..." * @param predefined A table of [key: string, value:string], where variables named 'key' will be renamed to 'value' */ -function unshadowVariables(program: es.Node, predefined = {}) { +function unshadowVariables(program: Node, predefined = {}) { for (const name of Object.values(globalIds)) { predefined[name] = name } @@ -232,7 +233,7 @@ function transformLogicalExpressions(program: es.Program) { * Changes -ary operations to functions that accept hybrid values as arguments. * E.g. "1+1" -> "functions.evalB('+',1,1)" */ -function hybridizeBinaryUnaryOperations(program: es.Node) { +function hybridizeBinaryUnaryOperations(program: Node) { simple(program, { BinaryExpression(node: es.BinaryExpression) { const { operator, left, right } = node @@ -252,7 +253,7 @@ function hybridizeBinaryUnaryOperations(program: es.Node) { }) } -function hybridizeVariablesAndLiterals(program: es.Node) { +function hybridizeVariablesAndLiterals(program: Node) { recursive(program, true, { Identifier(node: es.Identifier, state: boolean, _callback: WalkerCallback) { if (state) { @@ -298,7 +299,7 @@ function hybridizeVariablesAndLiterals(program: es.Node) { * For assignments to elements of arrays we concretize the RHS. * E.g. "a[1] = y;" -> "a[1] = concretize(y);" */ -function trackVariableAssignment(program: es.Node) { +function trackVariableAssignment(program: Node) { simple(program, { AssignmentExpression(node: es.AssignmentExpression) { if (node.left.type === 'Identifier') { @@ -357,7 +358,7 @@ function inPlaceEnclose(node: es.Statement, prepend?: es.Statement, append?: es. /** * Add tracking to if statements and conditional expressions in the state using saveTheTest. */ -function trackIfStatements(program: es.Node) { +function trackIfStatements(program: Node) { const theFunction = (node: es.IfStatement | es.ConditionalExpression) => saveTheTest(node) simple(program, { IfStatement: theFunction, ConditionalExpression: theFunction }) } @@ -373,7 +374,7 @@ function trackIfStatements(program: es.Node) { * exitLoop(state);" * Where postLoop should return the value of its (optional) second argument. */ -function trackLoops(program: es.Node) { +function trackLoops(program: Node) { const makeCallStatement = (name: FunctionNames, args: es.Expression[]) => create.expressionStatement(create.callExpression(callFunction(name), args)) const stateExpr = create.identifier(globalIds.stateId) @@ -415,7 +416,7 @@ function trackLoops(program: es.Node) { * }" * where returnFunction should return its first argument 'x'. */ -function trackFunctions(program: es.Node) { +function trackFunctions(program: Node) { const preFunction = (name: string, params: es.Pattern[]) => { const args = params .filter(x => x.type === 'Identifier') diff --git a/src/interpreter/closure.ts b/src/interpreter/closure.ts index ad0974c45..583824544 100644 --- a/src/interpreter/closure.ts +++ b/src/interpreter/closure.ts @@ -3,8 +3,8 @@ import { generate } from 'astring' import * as es from 'estree' import { uniqueId } from 'lodash' -import { hasReturnStatement, isBlockStatement } from '../cse-machine/utils' -import { Context, Environment, Value } from '../types' +import { hasReturnStatement, isBlockStatement, isStatementSequence } from '../cse-machine/utils' +import { Context, Environment, StatementSequence, Value } from '../types' import { blockArrowFunction, blockStatement, @@ -59,17 +59,18 @@ export default class Closure extends Callable { dummyReturn?: boolean, predefined?: boolean ) { - const functionBody: es.BlockStatement = !isBlockStatement(node.body) - ? blockStatement([returnStatement(node.body, node.body.loc)], node.body.loc) - : dummyReturn && !hasReturnStatement(node.body) - ? blockStatement( - [ - ...node.body.body, - returnStatement(identifier('undefined', node.body.loc), node.body.loc) - ], - node.body.loc - ) - : node.body + const functionBody: es.BlockStatement | StatementSequence = + !isBlockStatement(node.body) && !isStatementSequence(node.body) + ? blockStatement([returnStatement(node.body, node.body.loc)], node.body.loc) + : dummyReturn && !hasReturnStatement(node.body) + ? blockStatement( + [ + ...node.body.body, + returnStatement(identifier('undefined', node.body.loc), node.body.loc) + ], + node.body.loc + ) + : node.body const closure = new Closure( blockArrowFunction(node.params as es.Identifier[], functionBody, node.loc), diff --git a/src/interpreter/interpreter-non-det.ts b/src/interpreter/interpreter-non-det.ts index 570566d3f..f9fc8216e 100644 --- a/src/interpreter/interpreter-non-det.ts +++ b/src/interpreter/interpreter-non-det.ts @@ -5,7 +5,7 @@ import { cloneDeep, uniqueId } from 'lodash' import { CUT, UNKNOWN_LOCATION } from '../constants' import * as errors from '../errors/errors' import { RuntimeSourceError } from '../errors/runtimeSourceError' -import { Context, Environment, Frame, Value } from '../types' +import { Context, Environment, Frame, Node, Value } from '../types' import { conditionalExpression, literal, primitive } from '../utils/astCreator' import { evaluateBinaryExpression, evaluateUnaryExpression } from '../utils/operators' import * as rttc from '../utils/rttc' @@ -67,7 +67,7 @@ const handleRuntimeError = (context: Context, error: RuntimeSourceError): never const DECLARED_BUT_NOT_YET_ASSIGNED = Symbol('Used to implement declaration') -function declareIdentifier(context: Context, name: string, node: es.Node) { +function declareIdentifier(context: Context, name: string, node: Node) { const environment = currentEnvironment(context) if (environment.head.hasOwnProperty(name)) { const descriptors = Object.getOwnPropertyDescriptors(environment.head) @@ -208,10 +208,10 @@ function randomInt(min: number, max: number): number { } function* getAmbRArgs(context: Context, call: es.CallExpression) { - const args: es.Node[] = cloneDeep(call.arguments) + const args: Node[] = cloneDeep(call.arguments) while (args.length > 0) { const r = randomInt(0, args.length - 1) - const arg: es.Node = args.splice(r, 1)[0] + const arg: Node = args.splice(r, 1)[0] yield* evaluate(arg, context) } @@ -260,7 +260,7 @@ function transformLogicalExpression(node: es.LogicalExpression): es.ConditionalE function* reduceIf( node: es.IfStatement | es.ConditionalExpression, context: Context -): IterableIterator { +): IterableIterator { const testGenerator = evaluate(node.test, context) for (const test of testGenerator) { const error = rttc.checkIfStatement(node, test, context.chapter) @@ -271,7 +271,7 @@ function* reduceIf( } } -export type Evaluator = (node: T, context: Context) => IterableIterator +export type Evaluator = (node: T, context: Context) => IterableIterator function* evaluateBlockSatement(context: Context, node: es.BlockStatement) { declareFunctionAndVariableIdentifiers(context, node) @@ -334,7 +334,7 @@ function* evaluateConditional(node: es.IfStatement | es.ConditionalExpression, c */ // tslint:disable:object-literal-shorthand // prettier-ignore -export const evaluators: { [nodeType: string]: Evaluator } = { +export const evaluators: { [nodeType: string]: Evaluator } = { /** Simple Values */ Literal: function*(node: es.Literal, _context: Context) { yield node.value @@ -629,7 +629,7 @@ export const evaluators: { [nodeType: string]: Evaluator } = { } // tslint:enable:object-literal-shorthand -export function* evaluate(node: es.Node, context: Context) { +export function* evaluate(node: Node, context: Context) { const result = yield* evaluators[node.type](node, context) return result } diff --git a/src/interpreter/interpreter.ts b/src/interpreter/interpreter.ts index f883d3a95..8272a4d46 100644 --- a/src/interpreter/interpreter.ts +++ b/src/interpreter/interpreter.ts @@ -10,7 +10,15 @@ import { UndefinedImportError } from '../modules/errors' import { initModuleContext, loadModuleBundle } from '../modules/moduleLoader' import { ModuleFunctions } from '../modules/moduleTypes' import { checkEditorBreakpoints } from '../stdlib/inspector' -import { Context, ContiguousArrayElements, Environment, Frame, Value, Variant } from '../types' +import { + Context, + ContiguousArrayElements, + Environment, + Frame, + Node, + Value, + Variant +} from '../types' import assert from '../utils/assert' import * as create from '../utils/astCreator' import { conditionalExpression, literal, primitive } from '../utils/astCreator' @@ -33,13 +41,13 @@ class TailCallReturnValue { class Thunk { public value: Value public isMemoized: boolean - constructor(public exp: es.Node, public env: Environment) { + constructor(public exp: Node, public env: Environment) { this.isMemoized = false this.value = null } } -const delayIt = (exp: es.Node, env: Environment): Thunk => new Thunk(exp, env) +const delayIt = (exp: Node, env: Environment): Thunk => new Thunk(exp, env) function* forceIt(val: any, context: Context): Value { if (val instanceof Thunk) { @@ -54,7 +62,7 @@ function* forceIt(val: any, context: Context): Value { } else return val } -export function* actualValue(exp: es.Node, context: Context): Value { +export function* actualValue(exp: Node, context: Context): Value { const evalResult = yield* evaluate(exp, context) const forced = yield* forceIt(evalResult, context) return forced @@ -110,7 +118,7 @@ const handleRuntimeError = (context: Context, error: RuntimeSourceError): never const DECLARED_BUT_NOT_YET_ASSIGNED = Symbol('Used to implement block scope') -function declareIdentifier(context: Context, name: string, node: es.Node) { +function declareIdentifier(context: Context, name: string, node: Node) { const environment = currentEnvironment(context) if (environment.head.hasOwnProperty(name)) { const descriptors = Object.getOwnPropertyDescriptors(environment.head) @@ -167,7 +175,7 @@ function defineVariable(context: Context, name: string, value: Value, constant = return environment } -function* visit(context: Context, node: es.Node) { +function* visit(context: Context, node: Node) { checkEditorBreakpoints(context, node) context.runtime.nodes.unshift(node) yield context @@ -294,7 +302,7 @@ function transformLogicalExpression(node: es.LogicalExpression): es.ConditionalE function* reduceIf( node: es.IfStatement | es.ConditionalExpression, context: Context -): IterableIterator { +): IterableIterator { const test = yield* actualValue(node.test, context) const error = rttc.checkIfStatement(node, test, context.chapter) @@ -305,7 +313,7 @@ function* reduceIf( return test ? node.consequent : node.alternate } -export type Evaluator = (node: T, context: Context) => IterableIterator +export type Evaluator = (node: T, context: Context) => IterableIterator function* evaluateBlockStatement(context: Context, node: es.BlockStatement) { declareFunctionsAndVariables(context, node) @@ -336,7 +344,7 @@ function* evaluateBlockStatement(context: Context, node: es.BlockStatement) { */ // tslint:disable:object-literal-shorthand // prettier-ignore -export const evaluators: { [nodeType: string]: Evaluator } = { +export const evaluators: { [nodeType: string]: Evaluator } = { /** Simple Values */ Literal: function*(node: es.Literal, _context: Context) { return node.value @@ -781,7 +789,7 @@ export function* evaluateProgram( return result } -function* evaluate(node: es.Node, context: Context) { +function* evaluate(node: Node, context: Context) { yield* visit(context, node) const result = yield* evaluators[node.type](node, context) yield* leave(context) diff --git a/src/localImports/transformers/removeExports.ts b/src/localImports/transformers/removeExports.ts index eb2962d8b..d9e790a3e 100644 --- a/src/localImports/transformers/removeExports.ts +++ b/src/localImports/transformers/removeExports.ts @@ -1,5 +1,6 @@ import es from 'estree' +import { Node } from '../../types' import { isDeclaration } from '../../utils/ast/typeGuards' import { ancestor } from '../../utils/walkers' @@ -16,11 +17,7 @@ import { ancestor } from '../../utils/walkers' export const removeExports = (program: es.Program): void => { ancestor(program, { // TODO: Handle other export AST nodes. - ExportNamedDeclaration( - node: es.ExportNamedDeclaration, - _state: es.Node[], - ancestors: es.Node[] - ) { + ExportNamedDeclaration(node: es.ExportNamedDeclaration, _state: Node[], ancestors: Node[]) { // The ancestors array contains the current node, meaning that the // parent node is the second last node of the array. const parent = ancestors[ancestors.length - 2] @@ -38,11 +35,7 @@ export const removeExports = (program: es.Program): void => { parent.body.splice(nodeIndex, 1) } }, - ExportDefaultDeclaration( - node: es.ExportDefaultDeclaration, - _state: es.Node[], - ancestors: es.Node[] - ) { + ExportDefaultDeclaration(node: es.ExportDefaultDeclaration, _state: Node[], ancestors: Node[]) { // The ancestors array contains the current node, meaning that the // parent node is the second last node of the array. const parent = ancestors[ancestors.length - 2] diff --git a/src/localImports/transformers/removeNonSourceModuleImports.ts b/src/localImports/transformers/removeNonSourceModuleImports.ts index 61052de6c..0dc196a11 100644 --- a/src/localImports/transformers/removeNonSourceModuleImports.ts +++ b/src/localImports/transformers/removeNonSourceModuleImports.ts @@ -1,5 +1,6 @@ import es from 'estree' +import { Node } from '../../types' import assert from '../../utils/assert' import { ancestor } from '../../utils/walkers' import { isFilePath } from '../filePaths' @@ -38,13 +39,13 @@ export const isSourceModule = (moduleName: string): boolean => { export const removeNonSourceModuleImports = (program: es.Program): void => { // First pass: remove all import AST nodes which are unused by Source modules. ancestor(program, { - ImportSpecifier(_node: es.ImportSpecifier, _state: es.Node[], _ancestors: es.Node[]): void { + ImportSpecifier(_node: es.ImportSpecifier, _state: Node[], _ancestors: Node[]): void { // Nothing to do here since ImportSpecifier nodes are used by Source modules. }, ImportDefaultSpecifier( node: es.ImportDefaultSpecifier, - _state: es.Node[], - ancestors: es.Node[] + _state: Node[], + ancestors: Node[] ): void { // The ancestors array contains the current node, meaning that the // parent node is the second last node of the array. @@ -60,8 +61,8 @@ export const removeNonSourceModuleImports = (program: es.Program): void => { }, ImportNamespaceSpecifier( node: es.ImportNamespaceSpecifier, - _state: es.Node[], - ancestors: es.Node[] + _state: Node[], + ancestors: Node[] ): void { // The ancestors array contains the current node, meaning that the // parent node is the second last node of the array. @@ -79,7 +80,7 @@ export const removeNonSourceModuleImports = (program: es.Program): void => { // Operate on a copy of the Program node's body to prevent the walk from missing ImportDeclaration nodes. const programBody = [...program.body] - const removeImportDeclaration = (node: es.ImportDeclaration, ancestors: es.Node[]): void => { + const removeImportDeclaration = (node: es.ImportDeclaration, ancestors: Node[]): void => { // The ancestors array contains the current node, meaning that the // parent node is the second last node of the array. const parent = ancestors[ancestors.length - 2] @@ -94,7 +95,7 @@ export const removeNonSourceModuleImports = (program: es.Program): void => { // Second pass: remove all ImportDeclaration nodes for non-Source modules, or that do not // have any specifiers (thus being functionally useless). ancestor(program, { - ImportDeclaration(node: es.ImportDeclaration, _state: es.Node[], ancestors: es.Node[]): void { + ImportDeclaration(node: es.ImportDeclaration, _state: Node[], ancestors: Node[]): void { assert(typeof node.source.value === 'string', 'Module names must be strings.') // ImportDeclaration nodes without any specifiers are functionally useless and are thus removed. if (node.specifiers.length === 0) { diff --git a/src/modules/errors.ts b/src/modules/errors.ts index 4f69c1939..7b4d688be 100644 --- a/src/modules/errors.ts +++ b/src/modules/errors.ts @@ -1,6 +1,7 @@ import type * as es from 'estree' import { RuntimeSourceError } from '../errors/runtimeSourceError' +import { Node } from '../types' export class UndefinedImportError extends RuntimeSourceError { constructor( @@ -23,7 +24,7 @@ export class UndefinedImportError extends RuntimeSourceError { export class ModuleConnectionError extends RuntimeSourceError { private static message: string = `Unable to get modules.` private static elaboration: string = `You should check your Internet connection, and ensure you have used the correct module path.` - constructor(node?: es.Node) { + constructor(node?: Node) { super(node) } @@ -37,7 +38,7 @@ export class ModuleConnectionError extends RuntimeSourceError { } export class ModuleNotFoundError extends RuntimeSourceError { - constructor(public moduleName: string, node?: es.Node) { + constructor(public moduleName: string, node?: Node) { super(node) } @@ -53,7 +54,7 @@ export class ModuleNotFoundError extends RuntimeSourceError { } export class ModuleInternalError extends RuntimeSourceError { - constructor(public moduleName: string, public error?: any, node?: es.Node) { + constructor(public moduleName: string, public error?: any, node?: Node) { super(node) } diff --git a/src/modules/moduleLoader.ts b/src/modules/moduleLoader.ts index c4445a32b..9c415ecae 100644 --- a/src/modules/moduleLoader.ts +++ b/src/modules/moduleLoader.ts @@ -1,4 +1,3 @@ -import es from 'estree' import { memoize } from 'lodash' import { XMLHttpRequest as NodeXMLHttpRequest } from 'xmlhttprequest-ts' @@ -7,7 +6,7 @@ import { ModuleInternalError, ModuleNotFoundError } from '../errors/moduleErrors' -import { Context } from '../types' +import { Context, Node } from '../types' import { wrapSourceModule } from '../utils/operators' import { ModuleBundle, ModuleDocumentation, ModuleFunctions, ModuleManifest } from './moduleTypes' import { getRequireProvider } from './requireProvider' @@ -71,7 +70,7 @@ function getModuleFile({ name, type }: { name: string; type: 'tab' | 'bundle' | * @param node import declaration node * @returns the module's functions object */ -export function loadModuleBundle(path: string, context: Context, node?: es.Node): ModuleFunctions { +export function loadModuleBundle(path: string, context: Context, node?: Node): ModuleFunctions { const modules = memoizedGetModuleManifest() // Check if the module exists @@ -96,7 +95,7 @@ export function loadModuleBundle(path: string, context: Context, node?: es.Node) * @param node import declaration node * @returns an array of functions */ -export function loadModuleTabs(path: string, node?: es.Node) { +export function loadModuleTabs(path: string, node?: Node) { const modules = memoizedGetModuleManifest() // Check if the module exists const moduleList = Object.keys(modules) @@ -117,7 +116,7 @@ export function loadModuleTabs(path: string, node?: es.Node) { } export const memoizedloadModuleDocs = memoize(loadModuleDocs) -export function loadModuleDocs(path: string, node?: es.Node) { +export function loadModuleDocs(path: string, node?: Node) { try { const modules = memoizedGetModuleManifest() // Check if the module exists @@ -135,7 +134,7 @@ export function initModuleContext( moduleName: string, context: Context, loadTabs: boolean, - node?: es.Node + node?: Node ) { if (!(moduleName in context.moduleContexts)) { context.moduleContexts[moduleName] = { diff --git a/src/name-extractor/index.ts b/src/name-extractor/index.ts index dc197f6f9..3bf6c316a 100644 --- a/src/name-extractor/index.ts +++ b/src/name-extractor/index.ts @@ -7,6 +7,7 @@ import { ModuleConnectionError, ModuleNotFoundError } from '../errors/moduleErro import { findAncestors, findIdentifierNode } from '../finder' import { memoizedloadModuleDocs } from '../modules/moduleLoader' import syntaxBlacklist from '../parser/source/syntax' +import { Node } from '../types' export interface NameDeclaration { name: string @@ -20,15 +21,15 @@ const KIND_FUNCTION = 'func' const KIND_PARAM = 'param' const KIND_CONST = 'const' -function isImportDeclaration(node: es.Node): boolean { +function isImportDeclaration(node: Node): boolean { return node.type === 'ImportDeclaration' } -function isDeclaration(node: es.Node): boolean { +function isDeclaration(node: Node): boolean { return node.type === 'VariableDeclaration' || node.type === 'FunctionDeclaration' } -function isFunction(node: es.Node): boolean { +function isFunction(node: Node): boolean { return ( node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression' || @@ -36,7 +37,7 @@ function isFunction(node: es.Node): boolean { ) } -function isLoop(node: es.Node): boolean { +function isLoop(node: Node): boolean { return node.type === 'WhileStatement' || node.type === 'ForStatement' } @@ -79,7 +80,7 @@ const keywordsInFunction: { [key: string]: NameDeclaration[] } = { * @returns A list of keywords as suggestions */ export function getKeywords( - prog: es.Node, + prog: Node, cursorLoc: es.Position, context: Context ): NameDeclaration[] { @@ -141,7 +142,7 @@ export function getKeywords( * suggestions should be displayed, i.e. `[suggestions, shouldPrompt]` */ export function getProgramNames( - prog: es.Node, + prog: Node, comments: acorn.Comment[], cursorLoc: es.Position ): [NameDeclaration[], boolean] { @@ -164,8 +165,8 @@ export function getProgramNames( } // BFS to get names - const queue: es.Node[] = [prog] - const nameQueue: es.Node[] = [] + const queue: Node[] = [prog] + const nameQueue: Node[] = [] while (queue.length > 0) { // Workaround due to minification problem @@ -214,7 +215,7 @@ function isNotNull(x: T): x is Exclude { return x !== null } -function getNodeChildren(node: es.Node): es.Node[] { +function getNodeChildren(node: Node): Node[] { switch (node.type) { case 'Program': return node.body @@ -225,7 +226,7 @@ function getNodeChildren(node: es.Node): es.Node[] { case 'ForStatement': return [node.init, node.test, node.update, node.body].filter( n => n !== undefined && n !== null - ) as es.Node[] + ) as Node[] case 'ExpressionStatement': return [node.expression] case 'IfStatement': @@ -241,7 +242,7 @@ function getNodeChildren(node: es.Node): es.Node[] { case 'VariableDeclaration': return node.declarations .map(getNodeChildren) - .reduce((prev: es.Node[], cur: es.Node[]) => prev.concat(cur)) + .reduce((prev: Node[], cur: Node[]) => prev.concat(cur)) case 'VariableDeclarator': return node.init ? [node.init] : [] case 'ArrowFunctionExpression': @@ -280,7 +281,7 @@ function getNodeChildren(node: es.Node): es.Node[] { } } -function cursorInIdentifier(node: es.Node, locTest: (node: es.Node) => boolean): boolean { +function cursorInIdentifier(node: Node, locTest: (node: Node) => boolean): boolean { switch (node.type) { case 'VariableDeclaration': for (const decl of node.declarations) { @@ -302,11 +303,11 @@ function cursorInIdentifier(node: es.Node, locTest: (node: es.Node) => boolean): /** * Gets a list of `NameDeclarations` from the given node * @param node Node to search for names - * @param locTest Callback of type `(node: es.Node) => boolean`. Should return true if the cursor + * @param locTest Callback of type `(node: Node) => boolean`. Should return true if the cursor * is located within the node, false otherwise * @returns List of found names */ -function getNames(node: es.Node, locTest: (node: es.Node) => boolean): NameDeclaration[] { +function getNames(node: Node, locTest: (node: Node) => boolean): NameDeclaration[] { switch (node.type) { case 'ImportDeclaration': const specs = node.specifiers.filter(x => !isDummyName(x.local.name)) diff --git a/src/parser/errors.ts b/src/parser/errors.ts index 4c1e61754..8e6fa5604 100644 --- a/src/parser/errors.ts +++ b/src/parser/errors.ts @@ -1,6 +1,7 @@ -import { Node, SourceLocation } from 'estree' +import { SourceLocation } from 'estree' import { UNKNOWN_LOCATION } from '../constants' +import { Node } from '../types' import { ErrorSeverity, ErrorType, SourceError } from '../types' import { stripIndent } from '../utils/formatters' diff --git a/src/parser/source/index.ts b/src/parser/source/index.ts index 5ccf8c1d8..733bea392 100644 --- a/src/parser/source/index.ts +++ b/src/parser/source/index.ts @@ -1,8 +1,8 @@ import { parse as acornParse, Token, tokenizer } from 'acorn' -import { Node as ESNode, Program } from 'estree' +import * as es from 'estree' import { DEFAULT_ECMA_VERSION } from '../../constants' -import { Chapter, Context, Rule, SourceError, Variant } from '../../types' +import { Chapter, Context, Node, Rule, SourceError, Variant } from '../../types' import { ancestor, AncestorWalkerFn } from '../../utils/walkers' import { DisallowedConstructError, FatalSyntaxError } from '../errors' import { AcornOptions, Parser } from '../types' @@ -12,7 +12,7 @@ import syntaxBlacklist from './syntax' const combineAncestorWalkers = (w1: AncestorWalkerFn, w2: AncestorWalkerFn): AncestorWalkerFn => - (node: ESNode, state: TState, ancestors: ESNode[]) => { + (node: Node, state: TState, ancestors: Node[]) => { w1(node, state, ancestors) w2(node, state, ancestors) } @@ -40,12 +40,12 @@ export class SourceParser implements Parser { context: Context, options?: Partial, throwOnError?: boolean - ): Program | null { + ): es.Program | null { try { return acornParse( programStr, createAcornParserOptions(DEFAULT_ECMA_VERSION, context.errors, options) - ) as unknown as Program + ) as unknown as es.Program } catch (error) { if (error instanceof SyntaxError) { error = new FatalSyntaxError( @@ -61,10 +61,10 @@ export class SourceParser implements Parser { return null } - validate(ast: Program, context: Context, throwOnError?: boolean): boolean { + validate(ast: es.Program, context: Context, throwOnError?: boolean): boolean { const validationWalkers: Map> = new Map() this.getDisallowedSyntaxes().forEach((syntaxNodeName: string) => { - validationWalkers.set(syntaxNodeName, (node: ESNode, _state: any, _ancestors: [ESNode]) => { + validationWalkers.set(syntaxNodeName, (node: Node, _state: any, _ancestors: [Node]) => { if (node.type != syntaxNodeName) return const error: DisallowedConstructError = new DisallowedConstructError(node) @@ -77,11 +77,7 @@ export class SourceParser implements Parser { .map(rule => Object.entries(rule.checkers)) .flat() .forEach(([syntaxNodeName, checker]) => { - const langWalker: AncestorWalkerFn = ( - node: ESNode, - _state: any, - ancestors: ESNode[] - ) => { + const langWalker: AncestorWalkerFn = (node: Node, _state: any, ancestors: Node[]) => { const errors: SourceError[] = checker(node, ancestors) if (throwOnError && errors.length > 0) throw errors[0] @@ -97,7 +93,7 @@ export class SourceParser implements Parser { } }) - ancestor(ast as ESNode, mapToObj(validationWalkers), undefined, undefined) + ancestor(ast as Node, mapToObj(validationWalkers), undefined, undefined) return context.errors.length == 0 } @@ -113,9 +109,9 @@ export class SourceParser implements Parser { ) } - private getLangRules(): Rule[] { + private getLangRules(): Rule[] { return defaultRules.filter( - (rule: Rule) => + (rule: Rule) => !( (rule.disableFromChapter && this.chapter >= rule.disableFromChapter) || (rule.disableForVariants && rule.disableForVariants.includes(this.variant)) diff --git a/src/parser/source/rules/bracesAroundFor.ts b/src/parser/source/rules/bracesAroundFor.ts index 43cc66829..51cb34816 100644 --- a/src/parser/source/rules/bracesAroundFor.ts +++ b/src/parser/source/rules/bracesAroundFor.ts @@ -2,7 +2,7 @@ import { generate } from 'astring' import * as es from 'estree' import { UNKNOWN_LOCATION } from '../../../constants' -import { ErrorSeverity, ErrorType, Rule, SourceError } from '../../../types' +import { ErrorSeverity, ErrorType, Node, Rule, SourceError } from '../../../types' export class BracesAroundForError implements SourceError { public type = ErrorType.SYNTAX @@ -33,7 +33,7 @@ const bracesAroundFor: Rule = { name: 'braces-around-for', checkers: { - ForStatement(node: es.ForStatement, _ancestors: [es.Node]) { + ForStatement(node: es.ForStatement, _ancestors: [Node]) { if (node.body.type !== 'BlockStatement') { return [new BracesAroundForError(node)] } else { diff --git a/src/parser/source/rules/bracesAroundIfElse.ts b/src/parser/source/rules/bracesAroundIfElse.ts index 9f88da935..a9eef232f 100644 --- a/src/parser/source/rules/bracesAroundIfElse.ts +++ b/src/parser/source/rules/bracesAroundIfElse.ts @@ -2,7 +2,7 @@ import { generate } from 'astring' import * as es from 'estree' import { UNKNOWN_LOCATION } from '../../../constants' -import { ErrorSeverity, ErrorType, Rule, SourceError } from '../../../types' +import { ErrorSeverity, ErrorType, Node, Rule, SourceError } from '../../../types' import { stripIndent } from '../../../utils/formatters' export class BracesAroundIfElseError implements SourceError { @@ -73,7 +73,7 @@ const bracesAroundIfElse: Rule = { name: 'braces-around-if-else', checkers: { - IfStatement(node: es.IfStatement, _ancestors: [es.Node]) { + IfStatement(node: es.IfStatement, _ancestors: [Node]) { const errors: SourceError[] = [] if (node.consequent && node.consequent.type !== 'BlockStatement') { errors.push(new BracesAroundIfElseError(node, 'consequent')) diff --git a/src/parser/source/rules/bracesAroundWhile.ts b/src/parser/source/rules/bracesAroundWhile.ts index bda534dda..62bda7356 100644 --- a/src/parser/source/rules/bracesAroundWhile.ts +++ b/src/parser/source/rules/bracesAroundWhile.ts @@ -2,7 +2,7 @@ import { generate } from 'astring' import * as es from 'estree' import { UNKNOWN_LOCATION } from '../../../constants' -import { ErrorSeverity, ErrorType, Rule, SourceError } from '../../../types' +import { ErrorSeverity, ErrorType, Node, Rule, SourceError } from '../../../types' export class BracesAroundWhileError implements SourceError { public type = ErrorType.SYNTAX @@ -30,7 +30,7 @@ const bracesAroundWhile: Rule = { name: 'braces-around-while', checkers: { - WhileStatement(node: es.WhileStatement, _ancestors: [es.Node]) { + WhileStatement(node: es.WhileStatement, _ancestors: [Node]) { if (node.body.type !== 'BlockStatement') { return [new BracesAroundWhileError(node)] } else { diff --git a/src/parser/source/rules/index.ts b/src/parser/source/rules/index.ts index ab04cd6f1..241307b9f 100644 --- a/src/parser/source/rules/index.ts +++ b/src/parser/source/rules/index.ts @@ -1,6 +1,4 @@ -import * as es from 'estree' - -import { Rule } from '../../../types' +import { Node, Rule } from '../../../types' import bracesAroundFor from './bracesAroundFor' import bracesAroundIfElse from './bracesAroundIfElse' import bracesAroundWhile from './bracesAroundWhile' @@ -25,7 +23,7 @@ import noUpdateAssignment from './noUpdateAssignment' import noVar from './noVar' import singleVariableDeclaration from './singleVariableDeclaration' -const rules: Rule[] = [ +const rules: Rule[] = [ bracesAroundFor, bracesAroundIfElse, bracesAroundWhile, diff --git a/src/parser/source/rules/noDeclareMutable.ts b/src/parser/source/rules/noDeclareMutable.ts index abb465fff..af0d45b12 100644 --- a/src/parser/source/rules/noDeclareMutable.ts +++ b/src/parser/source/rules/noDeclareMutable.ts @@ -2,7 +2,7 @@ import { generate } from 'astring' import * as es from 'estree' import { UNKNOWN_LOCATION } from '../../../constants' -import { Chapter, ErrorSeverity, ErrorType, Rule, SourceError } from '../../../types' +import { Chapter, ErrorSeverity, ErrorType, Node, Rule, SourceError } from '../../../types' const mutableDeclarators = ['let', 'var'] @@ -35,7 +35,7 @@ const noDeclareMutable: Rule = { disableFromChapter: Chapter.SOURCE_3, checkers: { - VariableDeclaration(node: es.VariableDeclaration, _ancestors: [es.Node]) { + VariableDeclaration(node: es.VariableDeclaration, _ancestors: [Node]) { if (mutableDeclarators.includes(node.kind)) { return [new NoDeclareMutableError(node)] } else { diff --git a/src/parser/source/rules/noDotAbbreviation.ts b/src/parser/source/rules/noDotAbbreviation.ts index bbf8539c1..52a536871 100644 --- a/src/parser/source/rules/noDotAbbreviation.ts +++ b/src/parser/source/rules/noDotAbbreviation.ts @@ -1,7 +1,7 @@ import * as es from 'estree' import { UNKNOWN_LOCATION } from '../../../constants' -import { Chapter, ErrorSeverity, ErrorType, Rule, SourceError } from '../../../types' +import { Chapter, ErrorSeverity, ErrorType, Node, Rule, SourceError } from '../../../types' export class NoDotAbbreviationError implements SourceError { public type = ErrorType.SYNTAX @@ -29,7 +29,7 @@ const noDotAbbreviation: Rule = { disableFromChapter: Chapter.LIBRARY_PARSER, checkers: { - MemberExpression(node: es.MemberExpression, _ancestors: [es.Node]) { + MemberExpression(node: es.MemberExpression, _ancestors: [Node]) { if (!node.computed) { return [new NoDotAbbreviationError(node)] } else { diff --git a/src/parser/source/rules/noEval.ts b/src/parser/source/rules/noEval.ts index dbbce34bc..848748486 100644 --- a/src/parser/source/rules/noEval.ts +++ b/src/parser/source/rules/noEval.ts @@ -1,7 +1,7 @@ import * as es from 'estree' import { UNKNOWN_LOCATION } from '../../../constants' -import { ErrorSeverity, ErrorType, Rule, SourceError } from '../../../types' +import { ErrorSeverity, ErrorType, Node, Rule, SourceError } from '../../../types' export class NoEval implements SourceError { public type = ErrorType.SYNTAX @@ -26,7 +26,7 @@ const noEval: Rule = { name: 'no-eval', checkers: { - Identifier(node: es.Identifier, _ancestors: [es.Node]) { + Identifier(node: es.Identifier, _ancestors: [Node]) { if (node.name === 'eval') { return [new NoEval(node)] } else { diff --git a/src/parser/source/rules/noExportNamedDeclarationWithDefault.ts b/src/parser/source/rules/noExportNamedDeclarationWithDefault.ts index 4d4d3b7b5..f38be0681 100644 --- a/src/parser/source/rules/noExportNamedDeclarationWithDefault.ts +++ b/src/parser/source/rules/noExportNamedDeclarationWithDefault.ts @@ -2,7 +2,7 @@ import * as es from 'estree' import { UNKNOWN_LOCATION } from '../../../constants' import { defaultExportLookupName } from '../../../stdlib/localImport.prelude' -import { ErrorSeverity, ErrorType, Rule, SourceError } from '../../../types' +import { ErrorSeverity, ErrorType, Node, Rule, SourceError } from '../../../types' import syntaxBlacklist from '../syntax' export class NoExportNamedDeclarationWithDefaultError implements SourceError { @@ -29,7 +29,7 @@ const noExportNamedDeclarationWithDefault: Rule = { disableFromChapter: syntaxBlacklist['ExportDefaultDeclaration'], checkers: { - ExportNamedDeclaration(node: es.ExportNamedDeclaration, _ancestors: [es.Node]) { + ExportNamedDeclaration(node: es.ExportNamedDeclaration, _ancestors: [Node]) { const errors: NoExportNamedDeclarationWithDefaultError[] = [] node.specifiers.forEach((specifier: es.ExportSpecifier) => { if (specifier.exported.name === defaultExportLookupName) { diff --git a/src/parser/source/rules/noFunctionDeclarationWithoutIdentifier.ts b/src/parser/source/rules/noFunctionDeclarationWithoutIdentifier.ts index 9420bbf33..4278140b7 100644 --- a/src/parser/source/rules/noFunctionDeclarationWithoutIdentifier.ts +++ b/src/parser/source/rules/noFunctionDeclarationWithoutIdentifier.ts @@ -1,7 +1,7 @@ import * as es from 'estree' import { UNKNOWN_LOCATION } from '../../../constants' -import { ErrorSeverity, ErrorType, Rule, SourceError } from '../../../types' +import { ErrorSeverity, ErrorType, Node, Rule, SourceError } from '../../../types' export class NoFunctionDeclarationWithoutIdentifierError implements SourceError { public type = ErrorType.SYNTAX @@ -26,7 +26,7 @@ const noFunctionDeclarationWithoutIdentifier: Rule = { name: 'no-function-declaration-without-identifier', checkers: { - FunctionDeclaration(node: es.FunctionDeclaration, _ancestors: es.Node[]): SourceError[] { + FunctionDeclaration(node: es.FunctionDeclaration, _ancestors: Node[]): SourceError[] { if (node.id === null) { return [new NoFunctionDeclarationWithoutIdentifierError(node)] } diff --git a/src/parser/source/rules/noIfWithoutElse.ts b/src/parser/source/rules/noIfWithoutElse.ts index 16b6e429e..f233f94dc 100644 --- a/src/parser/source/rules/noIfWithoutElse.ts +++ b/src/parser/source/rules/noIfWithoutElse.ts @@ -2,7 +2,7 @@ import { generate } from 'astring' import * as es from 'estree' import { UNKNOWN_LOCATION } from '../../../constants' -import { Chapter, ErrorSeverity, ErrorType, Rule, SourceError } from '../../../types' +import { Chapter, ErrorSeverity, ErrorType, Node, Rule, SourceError } from '../../../types' import { stripIndent } from '../../../utils/formatters' export class NoIfWithoutElseError implements SourceError { @@ -34,7 +34,7 @@ const noIfWithoutElse: Rule = { name: 'no-if-without-else', disableFromChapter: Chapter.SOURCE_3, checkers: { - IfStatement(node: es.IfStatement, _ancestors: [es.Node]) { + IfStatement(node: es.IfStatement, _ancestors: [Node]) { if (!node.alternate) { return [new NoIfWithoutElseError(node)] } else { diff --git a/src/parser/source/rules/noImplicitDeclareUndefined.ts b/src/parser/source/rules/noImplicitDeclareUndefined.ts index a61197209..e6404ae47 100644 --- a/src/parser/source/rules/noImplicitDeclareUndefined.ts +++ b/src/parser/source/rules/noImplicitDeclareUndefined.ts @@ -1,7 +1,7 @@ import * as es from 'estree' import { UNKNOWN_LOCATION } from '../../../constants' -import { ErrorSeverity, ErrorType, Rule, SourceError } from '../../../types' +import { ErrorSeverity, ErrorType, Node, Rule, SourceError } from '../../../types' import { stripIndent } from '../../../utils/formatters' export class NoImplicitDeclareUndefinedError implements SourceError { @@ -34,7 +34,7 @@ const noImplicitDeclareUndefined: Rule = { name: 'no-implicit-declare-undefined', checkers: { - VariableDeclaration(node: es.VariableDeclaration, _ancestors: [es.Node]) { + VariableDeclaration(node: es.VariableDeclaration, _ancestors: [Node]) { const errors: SourceError[] = [] for (const decl of node.declarations) { if (!decl.init) { diff --git a/src/parser/source/rules/noImplicitReturnUndefined.ts b/src/parser/source/rules/noImplicitReturnUndefined.ts index f2c702d9c..bddc2f1ab 100644 --- a/src/parser/source/rules/noImplicitReturnUndefined.ts +++ b/src/parser/source/rules/noImplicitReturnUndefined.ts @@ -1,7 +1,7 @@ import * as es from 'estree' import { UNKNOWN_LOCATION } from '../../../constants' -import { ErrorSeverity, ErrorType, Rule, SourceError } from '../../../types' +import { ErrorSeverity, ErrorType, Node, Rule, SourceError } from '../../../types' import { stripIndent } from '../../../utils/formatters' export class NoImplicitReturnUndefinedError implements SourceError { @@ -32,7 +32,7 @@ const noImplicitReturnUndefined: Rule = { name: 'no-implicit-return-undefined', checkers: { - ReturnStatement(node: es.ReturnStatement, _ancestors: [es.Node]) { + ReturnStatement(node: es.ReturnStatement, _ancestors: [Node]) { if (!node.argument) { return [new NoImplicitReturnUndefinedError(node)] } else { diff --git a/src/parser/source/rules/noImportSpecifierWithDefault.ts b/src/parser/source/rules/noImportSpecifierWithDefault.ts index e776441bd..2afe088b8 100644 --- a/src/parser/source/rules/noImportSpecifierWithDefault.ts +++ b/src/parser/source/rules/noImportSpecifierWithDefault.ts @@ -2,7 +2,7 @@ import * as es from 'estree' import { UNKNOWN_LOCATION } from '../../../constants' import { defaultExportLookupName } from '../../../stdlib/localImport.prelude' -import { ErrorSeverity, ErrorType, Rule, SourceError } from '../../../types' +import { ErrorSeverity, ErrorType, Node, Rule, SourceError } from '../../../types' import syntaxBlacklist from '../syntax' export class NoImportSpecifierWithDefaultError implements SourceError { @@ -29,7 +29,7 @@ const noImportSpecifierWithDefault: Rule = { disableFromChapter: syntaxBlacklist['ImportDefaultSpecifier'], checkers: { - ImportSpecifier(node: es.ImportSpecifier, _ancestors: [es.Node]) { + ImportSpecifier(node: es.ImportSpecifier, _ancestors: [Node]) { if (node.imported.name === defaultExportLookupName) { return [new NoImportSpecifierWithDefaultError(node)] } diff --git a/src/parser/source/rules/noNull.ts b/src/parser/source/rules/noNull.ts index 95c6f2c7b..e2cf63c04 100644 --- a/src/parser/source/rules/noNull.ts +++ b/src/parser/source/rules/noNull.ts @@ -1,7 +1,7 @@ import * as es from 'estree' import { UNKNOWN_LOCATION } from '../../../constants' -import { Chapter, ErrorSeverity, ErrorType, Rule, SourceError } from '../../../types' +import { Chapter, ErrorSeverity, ErrorType, Node, Rule, SourceError } from '../../../types' export class NoNullError implements SourceError { public type = ErrorType.SYNTAX @@ -26,7 +26,7 @@ const noNull: Rule = { name: 'no-null', disableFromChapter: Chapter.SOURCE_2, checkers: { - Literal(node: es.Literal, _ancestors: [es.Node]) { + Literal(node: es.Literal, _ancestors: [Node]) { if (node.value === null) { return [new NoNullError(node)] } else { diff --git a/src/parser/source/rules/noSpreadInArray.ts b/src/parser/source/rules/noSpreadInArray.ts index b3a92661b..a7883ff35 100644 --- a/src/parser/source/rules/noSpreadInArray.ts +++ b/src/parser/source/rules/noSpreadInArray.ts @@ -1,7 +1,7 @@ import * as es from 'estree' import { UNKNOWN_LOCATION } from '../../../constants' -import { ErrorSeverity, ErrorType, Rule, SourceError } from '../../../types' +import { ErrorSeverity, ErrorType, Node, Rule, SourceError } from '../../../types' export class NoSpreadInArray implements SourceError { public type = ErrorType.SYNTAX @@ -26,7 +26,7 @@ const noSpreadInArray: Rule = { name: 'no-assignment-expression', checkers: { - SpreadElement(node: es.SpreadElement, ancestors: [es.Node]) { + SpreadElement(node: es.SpreadElement, ancestors: [Node]) { const parent = ancestors[ancestors.length - 2] if (parent.type === 'CallExpression') { diff --git a/src/parser/source/rules/noTemplateExpression.ts b/src/parser/source/rules/noTemplateExpression.ts index e2b2410c7..dd870fe6c 100644 --- a/src/parser/source/rules/noTemplateExpression.ts +++ b/src/parser/source/rules/noTemplateExpression.ts @@ -1,7 +1,7 @@ import * as es from 'estree' import { UNKNOWN_LOCATION } from '../../../constants' -import { ErrorSeverity, ErrorType, Rule, SourceError } from '../../../types' +import { ErrorSeverity, ErrorType, Node, Rule, SourceError } from '../../../types' export class NoTemplateExpressionError implements SourceError { public type = ErrorType.SYNTAX @@ -26,7 +26,7 @@ const noTemplateExpression: Rule = { name: 'no-template-expression', checkers: { - TemplateLiteral(node: es.TemplateLiteral, _ancestors: [es.Node]) { + TemplateLiteral(node: es.TemplateLiteral, _ancestors: [Node]) { if (node.expressions.length > 0) { return [new NoTemplateExpressionError(node)] } else { diff --git a/src/parser/source/rules/noUnspecifiedLiteral.ts b/src/parser/source/rules/noUnspecifiedLiteral.ts index 863ca103c..29dc16c4c 100644 --- a/src/parser/source/rules/noUnspecifiedLiteral.ts +++ b/src/parser/source/rules/noUnspecifiedLiteral.ts @@ -1,7 +1,7 @@ import * as es from 'estree' import { UNKNOWN_LOCATION } from '../../../constants' -import { ErrorSeverity, ErrorType, Rule, SourceError } from '../../../types' +import { ErrorSeverity, ErrorType, Node, Rule, SourceError } from '../../../types' const specifiedLiterals = ['boolean', 'string', 'number'] @@ -32,7 +32,7 @@ export class NoUnspecifiedLiteral implements SourceError { const noUnspecifiedLiteral: Rule = { name: 'no-unspecified-literal', checkers: { - Literal(node: es.Literal, _ancestors: [es.Node]) { + Literal(node: es.Literal, _ancestors: [Node]) { if (node.value !== null && !specifiedLiterals.includes(typeof node.value)) { return [new NoUnspecifiedLiteral(node)] } else { diff --git a/src/parser/source/rules/noUnspecifiedOperator.ts b/src/parser/source/rules/noUnspecifiedOperator.ts index f0f9503a7..2768936df 100644 --- a/src/parser/source/rules/noUnspecifiedOperator.ts +++ b/src/parser/source/rules/noUnspecifiedOperator.ts @@ -1,7 +1,7 @@ import * as es from 'estree' import { UNKNOWN_LOCATION } from '../../../constants' -import { ErrorSeverity, ErrorType, Rule, SourceError } from '../../../types' +import { ErrorSeverity, ErrorType, Node, Rule, SourceError } from '../../../types' export class NoUnspecifiedOperatorError implements SourceError { public type = ErrorType.SYNTAX @@ -29,7 +29,7 @@ const noUnspecifiedOperator: Rule = { name: 'no-unspecified-operator', checkers: { - BinaryExpression(node: es.BinaryExpression, _ancestors: [es.Node]) { + BinaryExpression(node: es.BinaryExpression, _ancestors: [Node]) { const permittedOperators = [ '+', '-', diff --git a/src/parser/source/rules/noUpdateAssignment.ts b/src/parser/source/rules/noUpdateAssignment.ts index cab505875..5429fc82d 100644 --- a/src/parser/source/rules/noUpdateAssignment.ts +++ b/src/parser/source/rules/noUpdateAssignment.ts @@ -2,7 +2,7 @@ import { generate } from 'astring' import * as es from 'estree' import { UNKNOWN_LOCATION } from '../../../constants' -import { ErrorSeverity, ErrorType, Rule, SourceError } from '../../../types' +import { ErrorSeverity, ErrorType, Node, Rule, SourceError } from '../../../types' export class NoUpdateAssignment implements SourceError { public type = ErrorType.SYNTAX @@ -37,7 +37,7 @@ const noUpdateAssignment: Rule = { name: 'no-update-assignment', checkers: { - AssignmentExpression(node: es.AssignmentExpression, _ancestors: [es.Node]) { + AssignmentExpression(node: es.AssignmentExpression, _ancestors: [Node]) { if (node.operator !== '=') { return [new NoUpdateAssignment(node)] } else { diff --git a/src/parser/source/rules/noVar.ts b/src/parser/source/rules/noVar.ts index 16f8bc8fd..e7c591e5f 100644 --- a/src/parser/source/rules/noVar.ts +++ b/src/parser/source/rules/noVar.ts @@ -2,7 +2,7 @@ import { generate } from 'astring' import * as es from 'estree' import { UNKNOWN_LOCATION } from '../../../constants' -import { ErrorSeverity, ErrorType, Rule, SourceError } from '../../../types' +import { ErrorSeverity, ErrorType, Node, Rule, SourceError } from '../../../types' export class NoVarError implements SourceError { public type = ErrorType.SYNTAX @@ -30,7 +30,7 @@ const noVar: Rule = { name: 'no-var', checkers: { - VariableDeclaration(node: es.VariableDeclaration, _ancestors: [es.Node]) { + VariableDeclaration(node: es.VariableDeclaration, _ancestors: [Node]) { if (node.kind === 'var') { return [new NoVarError(node)] } else { diff --git a/src/parser/source/rules/singleVariableDeclaration.ts b/src/parser/source/rules/singleVariableDeclaration.ts index 1cf6c5422..0f59c3fa4 100644 --- a/src/parser/source/rules/singleVariableDeclaration.ts +++ b/src/parser/source/rules/singleVariableDeclaration.ts @@ -2,7 +2,7 @@ import { generate } from 'astring' import * as es from 'estree' import { UNKNOWN_LOCATION } from '../../../constants' -import { ErrorSeverity, ErrorType, Rule, SourceError } from '../../../types' +import { ErrorSeverity, ErrorType, Node, Rule, SourceError } from '../../../types' export class MultipleDeclarationsError implements SourceError { public type = ErrorType.SYNTAX @@ -36,7 +36,7 @@ const singleVariableDeclaration: Rule = { name: 'single-variable-declaration', checkers: { - VariableDeclaration(node: es.VariableDeclaration, _ancestors: [es.Node]) { + VariableDeclaration(node: es.VariableDeclaration, _ancestors: [Node]) { if (node.declarations.length > 1) { return [new MultipleDeclarationsError(node)] } else { diff --git a/src/parser/source/rules/strictEquality.ts b/src/parser/source/rules/strictEquality.ts index ec5f66711..61e3b29c0 100644 --- a/src/parser/source/rules/strictEquality.ts +++ b/src/parser/source/rules/strictEquality.ts @@ -1,7 +1,7 @@ import * as es from 'estree' import { UNKNOWN_LOCATION } from '../../../constants' -import { ErrorSeverity, ErrorType, Rule, SourceError } from '../../../types' +import { ErrorSeverity, ErrorType, Node, Rule, SourceError } from '../../../types' export class StrictEqualityError implements SourceError { public type = ErrorType.SYNTAX @@ -30,7 +30,7 @@ const strictEquality: Rule = { name: 'strict-equality', checkers: { - BinaryExpression(node: es.BinaryExpression, _ancestors: [es.Node]) { + BinaryExpression(node: es.BinaryExpression, _ancestors: [Node]) { if (node.operator === '==' || node.operator === '!=') { return [new StrictEqualityError(node)] } else { diff --git a/src/runner/sourceRunner.ts b/src/runner/sourceRunner.ts index ee6e6c085..f7169f4d3 100644 --- a/src/runner/sourceRunner.ts +++ b/src/runner/sourceRunner.ts @@ -2,7 +2,7 @@ import type * as es from 'estree' import * as _ from 'lodash' import type { RawSourceMap } from 'source-map' -import type { IOptions, Result } from '..' +import { type IOptions, type Result } from '..' import { JSSLANG_PROPERTIES, UNKNOWN_LOCATION } from '../constants' import { CSEResultPromise, evaluate as CSEvaluate } from '../cse-machine/interpreter' import { ExceptionError } from '../errors/errors' @@ -249,6 +249,7 @@ export async function sourceRunner( determineExecutionMethod(theOptions, context, program, isVerboseErrorsEnabled) + // native, don't evaluate prelude if (context.executionMethod === 'native' && context.variant === Variant.NATIVE) { return await fullJSRunner(program, context, theOptions.importOptions) } @@ -265,6 +266,7 @@ export async function sourceRunner( return sourceRunner(program, context, isVerboseErrorsEnabled, options) } + // testing AST transform with CSE machine first if (context.variant === Variant.EXPLICIT_CONTROL) { return runCSEMachine(program, context, theOptions) } diff --git a/src/scope-refactoring.ts b/src/scope-refactoring.ts index a2b542494..b092ea80f 100644 --- a/src/scope-refactoring.ts +++ b/src/scope-refactoring.ts @@ -1,7 +1,7 @@ import * as es from 'estree' import { isInLoc } from './finder' -import { BlockFrame, DefinitionNode } from './types' +import { BlockFrame, DefinitionNode, Node } from './types' import { simple } from './utils/walkers' /** @@ -464,7 +464,7 @@ function isPartOf(curr: es.SourceLocation, enclosing: es.SourceLocation): boolea // Returns all nodes that are not part of any child BlockFrame. // ie direct children of the current BlockFrame -export function getNodeLocsInCurrentBlockFrame( +export function getNodeLocsInCurrentBlockFrame( nodes: E[], currentLoc: es.SourceLocation, blocks: BlockFrame[] diff --git a/src/stdlib/inspector.ts b/src/stdlib/inspector.ts index 6bb785031..85a6fecb3 100644 --- a/src/stdlib/inspector.ts +++ b/src/stdlib/inspector.ts @@ -1,7 +1,5 @@ -import { Node } from 'estree' - import { Context, Result } from '..' -import { Scheduler, Value } from '../types' +import { Node, Scheduler, Value } from '../types' export const saveState = ( context: Context, diff --git a/src/stdlib/parser.ts b/src/stdlib/parser.ts index f95cbc257..69a35a18c 100644 --- a/src/stdlib/parser.ts +++ b/src/stdlib/parser.ts @@ -3,7 +3,7 @@ import * as es from 'estree' import { parse as sourceParse } from '../parser/parser' import { SourceParser } from '../parser/source' import { libraryParserLanguage } from '../parser/source/syntax' -import { Context, ContiguousArrayElements, Value } from '../types' +import { Context, ContiguousArrayElements, Node, StatementSequence, Value } from '../types' import { oneLine } from '../utils/formatters' import { vector_to_list } from './list' @@ -28,13 +28,13 @@ function unreachable() { // can be represented by the element itself, // instead of constructing a sequence -function makeSequenceIfNeeded(exs: es.Node[]) { +function makeSequenceIfNeeded(exs: Node[]) { return exs.length === 1 ? transform(exs[0]) : vector_to_list(['sequence', vector_to_list(exs.map(transform))]) } -function makeBlockIfNeeded(exs: es.Node[]) { +function makeBlockIfNeeded(exs: Node[]) { return hasDeclarationAtToplevel(exs) ? vector_to_list(['block', makeSequenceIfNeeded(exs)]) : makeSequenceIfNeeded(exs) @@ -42,19 +42,19 @@ function makeBlockIfNeeded(exs: es.Node[]) { // checks if sequence has declaration at toplevel // (outside of any block) -function hasDeclarationAtToplevel(exs: es.Node[]) { +function hasDeclarationAtToplevel(exs: Node[]) { return exs.reduce( (b, ex) => b || ex.type === 'VariableDeclaration' || ex.type === 'FunctionDeclaration', false ) } -type ASTTransformers = Map Value> +type ASTTransformers = Map Value> const transformers: ASTTransformers = new Map([ [ 'Program', - (node: es.Node) => { + (node: Node) => { node = node as es.Program return makeSequenceIfNeeded(node.body) } @@ -67,6 +67,13 @@ const transformers: ASTTransformers = new Map([ } ], + [ + 'StatementSequence', + (node: StatementSequence) => { + return makeSequenceIfNeeded(node.body) + } + ], + [ 'ExpressionStatement', (node: es.ExpressionStatement) => { @@ -471,9 +478,9 @@ const transformers: ASTTransformers = new Map([ ] ]) -function transform(node: es.Node) { +function transform(node: Node) { if (transformers.has(node.type)) { - const transformer = transformers.get(node.type) as (n: es.Node) => Value + const transformer = transformers.get(node.type) as (n: Node) => Value const transformed = transformer(node) // Attach location information if ( diff --git a/src/stepper/util.ts b/src/stepper/util.ts index 1370b509c..fb9fb7e8f 100644 --- a/src/stepper/util.ts +++ b/src/stepper/util.ts @@ -3,7 +3,7 @@ import * as es from 'estree' import { Context } from '..' import * as errors from '../errors/errors' import { RuntimeSourceError } from '../errors/runtimeSourceError' -import { Environment, Value } from '../types' +import { Environment, Node, Value } from '../types' import { BlockExpression, substituterNodes } from '../types' import * as builtin from './lib' @@ -81,7 +81,7 @@ const DECLARED_BUT_NOT_YET_ASSIGNED = Symbol('Used to implement hoisting') export function declareIdentifier( context: Context, name: string, - node: es.Node, + node: Node, environment: Environment ) { if (environment.head.hasOwnProperty(name)) { diff --git a/src/transpiler/transpiler.ts b/src/transpiler/transpiler.ts index 9614c02f1..bce1d655f 100644 --- a/src/transpiler/transpiler.ts +++ b/src/transpiler/transpiler.ts @@ -21,7 +21,9 @@ import { Chapter, Context, NativeStorage, + Node, RecursivePartial, + StatementSequence, Variant } from '../types' import assert from '../utils/assert' @@ -204,7 +206,7 @@ export function evallerReplacer( } function generateFunctionsToStringMap(program: es.Program) { - const map: Map = new Map() + const map: Map = new Map() simple(program, { ArrowFunctionExpression(node: es.ArrowFunctionExpression) { map.set(node, generate(node)) @@ -218,7 +220,7 @@ function generateFunctionsToStringMap(program: es.Program) { function transformFunctionDeclarationsToArrowFunctions( program: es.Program, - functionsToStringMap: Map + functionsToStringMap: Map ) { simple(program, { FunctionDeclaration(node) { @@ -254,7 +256,7 @@ function transformFunctionDeclarationsToArrowFunctions( function wrapArrowFunctionsToAllowNormalCallsAndNiceToString( program: es.Program, - functionsToStringMap: Map, + functionsToStringMap: Map, globalIds: NativeIds ) { simple(program, { @@ -366,7 +368,7 @@ export function checkForUndefinedVariables( skipUndefined: boolean ) { const builtins = nativeStorage.builtins - const identifiersIntroducedByNode = new Map>() + const identifiersIntroducedByNode = new Map>() function processBlock(node: es.Program | es.BlockStatement) { const identifiers = new Set() for (const statement of node.body) { @@ -389,7 +391,7 @@ export function checkForUndefinedVariables( } function processFunction( node: es.FunctionDeclaration | es.ArrowFunctionExpression, - _ancestors: es.Node[] + _ancestors: Node[] ) { identifiersIntroducedByNode.set( node, @@ -402,13 +404,13 @@ export function checkForUndefinedVariables( ) ) } - const identifiersToAncestors = new Map() + const identifiersToAncestors = new Map() ancestor(program, { Program: processBlock, BlockStatement: processBlock, FunctionDeclaration: processFunction, ArrowFunctionExpression: processFunction, - ForStatement(forStatement: es.ForStatement, ancestors: es.Node[]) { + ForStatement(forStatement: es.ForStatement, ancestors: Node[]) { const init = forStatement.init! if (init.type === 'VariableDeclaration') { identifiersIntroducedByNode.set( @@ -417,10 +419,10 @@ export function checkForUndefinedVariables( ) } }, - Identifier(identifier: es.Identifier, ancestors: es.Node[]) { + Identifier(identifier: es.Identifier, ancestors: Node[]) { identifiersToAncestors.set(identifier, [...ancestors]) }, - Pattern(node: es.Pattern, ancestors: es.Node[]) { + Pattern(node: es.Pattern, ancestors: Node[]) { if (node.type === 'Identifier') { identifiersToAncestors.set(node, [...ancestors]) } else if (node.type === 'MemberExpression') { @@ -467,8 +469,8 @@ export function checkProgramForUndefinedVariables(program: es.Program, context: const builtins = context.nativeStorage.builtins const env = context.runtime.environments[0].head - const identifiersIntroducedByNode = new Map>() - function processBlock(node: es.Program | es.BlockStatement) { + const identifiersIntroducedByNode = new Map>() + function processBlock(node: es.Program | es.BlockStatement | StatementSequence) { const identifiers = new Set() for (const statement of node.body) { if (statement.type === 'VariableDeclaration') { @@ -490,7 +492,7 @@ export function checkProgramForUndefinedVariables(program: es.Program, context: } function processFunction( node: es.FunctionDeclaration | es.ArrowFunctionExpression, - _ancestors: es.Node[] + _ancestors: Node[] ) { identifiersIntroducedByNode.set( node, @@ -503,13 +505,14 @@ export function checkProgramForUndefinedVariables(program: es.Program, context: ) ) } - const identifiersToAncestors = new Map() + const identifiersToAncestors = new Map() ancestor(program, { Program: processBlock, BlockStatement: processBlock, + StatementSequence: processBlock, FunctionDeclaration: processFunction, ArrowFunctionExpression: processFunction, - ForStatement(forStatement: es.ForStatement, ancestors: es.Node[]) { + ForStatement(forStatement: es.ForStatement, ancestors: Node[]) { const init = forStatement.init! if (init.type === 'VariableDeclaration') { identifiersIntroducedByNode.set( @@ -518,10 +521,10 @@ export function checkProgramForUndefinedVariables(program: es.Program, context: ) } }, - Identifier(identifier: es.Identifier, ancestors: es.Node[]) { + Identifier(identifier: es.Identifier, ancestors: Node[]) { identifiersToAncestors.set(identifier, [...ancestors]) }, - Pattern(node: es.Pattern, ancestors: es.Node[]) { + Pattern(node: es.Pattern, ancestors: Node[]) { if (node.type === 'Identifier') { identifiersToAncestors.set(node, [...ancestors]) } else if (node.type === 'MemberExpression') { diff --git a/src/typeChecker/internalTypeErrors.ts b/src/typeChecker/internalTypeErrors.ts index b5884413d..6afd332e7 100644 --- a/src/typeChecker/internalTypeErrors.ts +++ b/src/typeChecker/internalTypeErrors.ts @@ -1,7 +1,6 @@ -import * as es from 'estree' - import { UNKNOWN_LOCATION } from '../constants' import { ErrorSeverity, ErrorType, NodeWithInferredType, SourceError, Type } from '../types' +import { Node } from '../types' import { typeToString } from '../utils/stringify' import * as tsEs from './tsESTree' @@ -10,7 +9,7 @@ export class TypeError implements SourceError { public type = ErrorType.TYPE public severity = ErrorSeverity.WARNING - constructor(public node: NodeWithInferredType, public message: string) { + constructor(public node: NodeWithInferredType, public message: string) { node.typability = 'Untypable' } diff --git a/src/types.ts b/src/types.ts index 0459d903a..27a01984f 100644 --- a/src/types.ts +++ b/src/types.ts @@ -43,12 +43,12 @@ export interface SourceError { elaborate(): string } -export interface Rule { +export interface Rule { name: string disableFromChapter?: Chapter disableForVariants?: Variant[] checkers: { - [name: string]: (node: T, ancestors: es.Node[]) => SourceError[] + [name: string]: (node: T, ancestors: Node[]) => SourceError[] } } @@ -145,7 +145,7 @@ export interface Context { isRunning: boolean environmentTree: EnvTree environments: Environment[] - nodes: es.Node[] + nodes: Node[] control: Control | null stash: Stash | null envStepsTotal: number @@ -291,6 +291,23 @@ export interface Scheduler { run(it: IterableIterator, context: Context): Promise } +/** + * StatementSequence : A sequence of statements not surrounded by braces. + * It is *not* a block, and thus does not trigger environment creation when evaluated. + * + * The current ESTree specification does not have this node type, so we define it here. + */ +export interface StatementSequence extends es.BaseStatement { + type: 'StatementSequence' + body: Array + innerComments?: Array | undefined +} + +/** + * js-slang's custom Node type - this should be used wherever es.Node is used. + */ +export type Node = es.Node | StatementSequence + /* Although the ESTree specifications supposedly provide a Directive interface, the index file does not seem to export it. As such this interface was created here to fulfil the same purpose. @@ -319,15 +336,7 @@ export interface BlockExpression extends es.BaseExpression { body: es.Statement[] } -export type substituterNodes = es.Node | BlockExpression - -/** - * For use in the CSE machine: block statements are handled in two steps: - * environment creation, then unpacking - */ -export interface RawBlockStatement extends es.BlockStatement { - isRawBlock: 'true' -} +export type substituterNodes = Node | BlockExpression export { Instruction as SVMInstruction, @@ -358,7 +367,7 @@ export type TSDisallowedTypes = (typeof disallowedTypes)[number] export type TSBasicType = PrimitiveType | TSAllowedTypes | TSDisallowedTypes // Types for nodes used in type inference -export type NodeWithInferredType = InferredType & T +export type NodeWithInferredType = InferredType & T export type FuncDeclWithInferredTypeAnnotation = NodeWithInferredType & TypedFuncDecl diff --git a/src/utils/ast/typeGuards.ts b/src/utils/ast/typeGuards.ts index 66e14f8b3..2b9ee2146 100644 --- a/src/utils/ast/typeGuards.ts +++ b/src/utils/ast/typeGuards.ts @@ -1,5 +1,7 @@ import type * as es from 'estree' +import { Node } from '../../types' + export const isImportDeclaration = (node: es.Program['body'][0]): node is es.ImportDeclaration => node.type === 'ImportDeclaration' @@ -19,11 +21,11 @@ export const isImportDeclaration = (node: es.Program['body'][0]): node is es.Imp // // As such, we check whether the 'directive' property exists on the object // instead in order to differentiate between the two. -export const isDirective = (node: es.Node): node is es.Directive => { +export const isDirective = (node: Node): node is es.Directive => { return 'directive' in node } -export const isModuleDeclaration = (node: es.Node): node is es.ModuleDeclaration => { +export const isModuleDeclaration = (node: Node): node is es.ModuleDeclaration => { return [ 'ImportDeclaration', 'ExportNamedDeclaration', @@ -38,7 +40,7 @@ export const isStatement = ( return !isDirective(node) && !isModuleDeclaration(node) } -export function isDeclaration(node: es.Node): node is es.Declaration { +export function isDeclaration(node: Node): node is es.Declaration { // export type Declaration = // FunctionDeclaration | VariableDeclaration | ClassDeclaration; return ( diff --git a/src/utils/astCreator.ts b/src/utils/astCreator.ts index 9943a82a5..f8216ae4d 100644 --- a/src/utils/astCreator.ts +++ b/src/utils/astCreator.ts @@ -1,5 +1,6 @@ import * as es from 'estree' +import { Node, StatementSequence } from '../types' import { AllowedDeclarations, BlockExpression, FunctionDeclarationExpression } from '../types' export const getVariableDecarationName = (decl: es.VariableDeclaration) => @@ -112,6 +113,15 @@ export const blockStatement = ( loc }) +export const statementSequence = ( + body: es.Statement[], + loc?: es.SourceLocation | null +): StatementSequence => ({ + type: 'StatementSequence', + body, + loc +}) + export const program = (body: es.Statement[]): es.Program => ({ type: 'Program', sourceType: 'module', @@ -143,7 +153,7 @@ export const objectExpression = (properties: es.Property[]): es.ObjectExpression }) export const mutateToCallExpression = ( - node: es.Node, + node: Node, callee: es.Expression, args: es.Expression[] ) => { @@ -154,7 +164,7 @@ export const mutateToCallExpression = ( } export const mutateToAssignmentExpression = ( - node: es.Node, + node: Node, left: es.Pattern, right: es.Expression ) => { @@ -165,23 +175,19 @@ export const mutateToAssignmentExpression = ( node.right = right } -export const mutateToExpressionStatement = (node: es.Node, expr: es.Expression) => { +export const mutateToExpressionStatement = (node: Node, expr: es.Expression) => { node.type = 'ExpressionStatement' node = node as es.ExpressionStatement node.expression = expr } -export const mutateToReturnStatement = (node: es.Node, expr: es.Expression) => { +export const mutateToReturnStatement = (node: Node, expr: es.Expression) => { node.type = 'ReturnStatement' node = node as es.ReturnStatement node.argument = expr } -export const mutateToMemberExpression = ( - node: es.Node, - obj: es.Expression, - prop: es.Expression -) => { +export const mutateToMemberExpression = (node: Node, obj: es.Expression, prop: es.Expression) => { node.type = 'MemberExpression' node = node as es.MemberExpression node.object = obj @@ -203,7 +209,7 @@ export const logicalExpression = ( }) export const mutateToConditionalExpression = ( - node: es.Node, + node: Node, test: es.Expression, consequent: es.Expression, alternate: es.Expression diff --git a/src/utils/astToString.ts b/src/utils/astToString.ts index e34a7cfe4..f447d5f22 100644 --- a/src/utils/astToString.ts +++ b/src/utils/astToString.ts @@ -1,4 +1,84 @@ -import { generate } from 'astring' -import { Node } from 'estree' +import * as astring from 'astring' -export const astToString = (node: Node): string => generate(node) +import { Node } from '../types' + +/** + * Writes into `state` the `text` string reindented with the provided `indent`. + */ +function reindent(state: any, text: any, indent: any, lineEnd: any) { + const lines = text.split('\n') + const end = lines.length - 1 + state.write(lines[0].trim()) + if (end > 0) { + state.write(lineEnd) + for (let i = 1; i < end; i++) { + state.write(indent + lines[i].trim() + lineEnd) + } + state.write(indent + lines[end].trim()) + } +} + +/** + * Writes into `state` the provided list of `comments`, with the given `indent` and `lineEnd` strings. + * Line comments will end with `"\n"` regardless of the value of `lineEnd`. + * Expects to start on a new unindented line. + */ +function formatComments(state: any, comments: any, indent: any, lineEnd: any) { + const { length } = comments + for (let i = 0; i < length; i++) { + const comment = comments[i] + state.write(indent) + if (comment.type[0] === 'L') { + // Line comment + state.write('// ' + comment.value.trim() + '\n', comment) + } else { + // Block comment + state.write('/*') + reindent(state, comment.value, indent, lineEnd) + state.write('*/' + lineEnd) + } + } +} + +const sourceGen = Object.assign({}, astring.GENERATOR, { + StatementSequence: function (node: any, state: any) { + const indent = state.indent.repeat(state.indentLevel++) + const { lineEnd, writeComments } = state + const statementIndent = indent + state.indent + state.write('[') + const statements = node.body + if (statements != null && statements.length > 0) { + state.write(lineEnd) + if (writeComments && node.comments != null) { + formatComments(state, node.comments, statementIndent, lineEnd) + } + const { length } = statements + for (let i = 0; i < length; i++) { + const statement = statements[i] + if (writeComments && statement.comments != null) { + formatComments(state, statement.comments, statementIndent, lineEnd) + } + state.write(statementIndent) + this[statement.type](statement, state) + state.write(lineEnd) + } + state.write(indent) + } else { + if (writeComments && node.comments != null) { + state.write(lineEnd) + formatComments(state, node.comments, statementIndent, lineEnd) + state.write(indent) + } + } + if (writeComments && node.trailingComments != null) { + formatComments(state, node.trailingComments, statementIndent, lineEnd) + } + state.write(']') + state.indentLevel-- + } +}) + +export const astToString = (node: Node): string => + astring.generate(node, { + generator: sourceGen + }) diff --git a/src/utils/dummyAstCreator.ts b/src/utils/dummyAstCreator.ts index 4784f4a3d..c8a652c29 100644 --- a/src/utils/dummyAstCreator.ts +++ b/src/utils/dummyAstCreator.ts @@ -86,7 +86,7 @@ export const objectExpression = (properties: es.Property[]): es.ObjectExpression }) export const mutateToCallExpression = ( - node: es.Node, + node: Node, callee: es.Expression, args: es.Expression[] ) => { diff --git a/src/utils/rttc.ts b/src/utils/rttc.ts index ab89dac6b..570ea8cc4 100644 --- a/src/utils/rttc.ts +++ b/src/utils/rttc.ts @@ -1,7 +1,7 @@ import * as es from 'estree' import { RuntimeSourceError } from '../errors/runtimeSourceError' -import { Chapter, ErrorSeverity, ErrorType, Value } from '../types' +import { Chapter, ErrorSeverity, ErrorType, Node, Value } from '../types' const LHS = ' on left hand side of operation' const RHS = ' on right hand side of operation' @@ -12,7 +12,7 @@ export class TypeError extends RuntimeSourceError { public location: es.SourceLocation constructor( - node: es.Node, + node: Node, public side: string, public expected: string, public got: string, @@ -54,7 +54,7 @@ const isObject = (v: Value) => typeOf(v) === 'object' const isArray = (v: Value) => typeOf(v) === 'array' export const checkUnaryExpression = ( - node: es.Node, + node: Node, operator: es.UnaryOperator, value: Value, chapter: Chapter = Chapter.SOURCE_4 @@ -69,7 +69,7 @@ export const checkUnaryExpression = ( } export const checkBinaryExpression = ( - node: es.Node, + node: Node, operator: es.BinaryOperator, chapter: Chapter, left: Value, @@ -113,17 +113,13 @@ export const checkBinaryExpression = ( } } -export const checkIfStatement = ( - node: es.Node, - test: Value, - chapter: Chapter = Chapter.SOURCE_4 -) => { +export const checkIfStatement = (node: Node, test: Value, chapter: Chapter = Chapter.SOURCE_4) => { return isBool(test) ? undefined : new TypeError(node, ' as condition', 'boolean', typeOf(test), chapter) } -export const checkMemberAccess = (node: es.Node, obj: Value, prop: Value) => { +export const checkMemberAccess = (node: Node, obj: Value, prop: Value) => { if (isObject(obj)) { return isString(prop) ? undefined : new TypeError(node, ' as prop', 'string', typeOf(prop)) } else if (isArray(obj)) { diff --git a/src/utils/statementSeqTransform.ts b/src/utils/statementSeqTransform.ts new file mode 100644 index 000000000..39afa7798 --- /dev/null +++ b/src/utils/statementSeqTransform.ts @@ -0,0 +1,435 @@ +import * as es from 'estree' + +import { Node, StatementSequence } from '../types' +import * as ast from './astCreator' +function hasDeclarations(node: es.BlockStatement | es.Program): boolean { + for (const statement of node.body) { + if (statement.type === 'VariableDeclaration' || statement.type === 'FunctionDeclaration') { + return true + } + } + return false +} + +function hasImportDeclarations(node: es.Program): boolean { + for (const statement of node.body) { + if (statement.type === 'ImportDeclaration') { + return true + } + } + return false +} + +type NodeTransformer = (node: Node) => Node + +type ASTTransformers = Map + +const transformers: ASTTransformers = new Map([ + [ + 'Program', + (node: es.Program) => { + if (hasDeclarations(node) || hasImportDeclarations(node)) { + return node + } else { + return ast.statementSequence(node.body as es.Statement[], node.loc) + } + } + ], + + [ + 'BlockStatement', + (node: es.BlockStatement) => { + node.body = node.body.map(x => transform(x)) + if (hasDeclarations(node)) { + return node + } else { + return ast.statementSequence(node.body, node.loc) + } + } + ], + + [ + 'StatementSequence', + (node: StatementSequence) => { + node.body = node.body.map(x => transform(x)) + return node + } + ], + + [ + 'ExpressionStatement', + (node: es.ExpressionStatement) => { + node.expression = transform(node.expression) + return node + } + ], + + [ + 'IfStatement', + (node: es.IfStatement) => { + node.test = transform(node.test) + node.consequent = transform(node.consequent) + if (node.alternate) { + node.alternate = transform(node.alternate) + } + return node + } + ], + + [ + 'FunctionDeclaration', + (node: es.FunctionDeclaration) => { + node.params = node.params.map(x => transform(x)) + node.body = transform(node.body) + if (node.id) { + node.id = transform(node.id) + } + return node + } + ], + + [ + 'VariableDeclarator', + (node: es.VariableDeclarator) => { + node.id = transform(node.id) + if (node.init) { + node.init = transform(node.init) + } + return node + } + ], + + [ + 'VariableDeclaration', + (node: es.VariableDeclaration) => { + node.declarations = node.declarations.map(x => transform(x)) + return node + } + ], + + [ + 'ReturnStatement', + (node: es.ReturnStatement) => { + if (node.argument) { + node.argument = transform(node.argument) + } + return node + } + ], + + [ + 'CallExpression', + (node: es.SimpleCallExpression) => { + node.callee = transform(node.callee) + node.arguments = node.arguments.map(x => transform(x)) + return node + } + ], + + [ + 'UnaryExpression', + (node: es.UnaryExpression) => { + node.argument = transform(node.argument) + return node + } + ], + + [ + 'BinaryExpression', + (node: es.BinaryExpression) => { + node.left = transform(node.left) + node.right = transform(node.right) + return node + } + ], + + [ + 'LogicalExpression', + (node: es.LogicalExpression) => { + node.left = transform(node.left) + node.right = transform(node.right) + return node + } + ], + + [ + 'ConditionalExpression', + (node: es.ConditionalExpression) => { + node.test = transform(node.test) + node.consequent = transform(node.consequent) + node.alternate = transform(node.alternate) + return node + } + ], + + [ + 'ArrowFunctionExpression', + (node: es.ArrowFunctionExpression) => { + node.params = node.params.map(x => transform(x)) + node.body = transform(node.body) + return node + } + ], + + [ + 'Identifier', + (node: es.Identifier) => { + return node + } + ], + + [ + 'Literal', + (node: es.Literal) => { + return node + } + ], + + [ + 'ArrayExpression', + (node: es.ArrayExpression) => { + node.elements = node.elements.map(x => (x ? transform(x) : null)) + return node + } + ], + + [ + 'AssignmentExpression', + (node: es.AssignmentExpression) => { + node.left = transform(node.left) + node.right = transform(node.right) + return node + } + ], + + [ + 'ForStatement', + (node: es.ForStatement) => { + if (node.init) { + node.init = transform(node.init) + } + if (node.test) { + node.test = transform(node.test) + } + if (node.update) { + node.update = transform(node.update) + } + node.body = transform(node.body) + return node + } + ], + + [ + 'WhileStatement', + (node: es.WhileStatement) => { + node.test = transform(node.test) + node.body = transform(node.body) + return node + } + ], + + [ + 'BreakStatement', + (node: es.BreakStatement) => { + if (node.label) { + node.label = transform(node.label) + } + return node + } + ], + + [ + 'ContinueStatement', + (node: es.ContinueStatement) => { + if (node.label) { + node.label = transform(node.label) + } + return node + } + ], + + [ + 'ObjectExpression', + (node: es.ObjectExpression) => { + node.properties = node.properties.map(x => transform(x)) + return node + } + ], + + [ + 'MemberExpression', + (node: es.MemberExpression) => { + node.object = transform(node.object) + node.property = transform(node.property) + return node + } + ], + + [ + 'Property', + (node: es.Property) => { + node.key = transform(node.key) + node.value = transform(node.value) + return node + } + ], + + [ + 'ImportDeclaration', + (node: es.ImportDeclaration) => { + node.specifiers = node.specifiers.map(x => transform(x)) + node.source = transform(node.source) + return node + } + ], + + [ + 'ImportSpecifier', + (node: es.ImportSpecifier) => { + node.local = transform(node.local) + node.imported = transform(node.imported) + return node + } + ], + + [ + 'ImportDefaultSpecifier', + (node: es.ImportDefaultSpecifier) => { + node.local = transform(node.local) + return node + } + ], + + [ + 'ExportNamedDeclaration', + (node: es.ExportNamedDeclaration) => { + if (node.declaration) { + node.declaration = transform(node.declaration) + } + node.specifiers = node.specifiers.map(x => transform(x)) + if (node.source) { + transform(node.source) + } + return node + } + ], + + [ + 'ExportDefaultDeclaration', + (node: es.ExportDefaultDeclaration) => { + node.declaration = transform(node.declaration) + return node + } + ], + + [ + 'ExportSpecifier', + (node: es.ExportSpecifier) => { + node.local = transform(node.local) + node.exported = transform(node.exported) + return node + } + ], + + [ + 'ClassDeclaration', + (node: es.ClassDeclaration) => { + if (node.id) { + node.id = transform(node.id) + } + if (node.superClass) { + node.superClass = transform(node.superClass) + } + node.body = transform(node.body) + return node + } + ], + + [ + 'NewExpression', + (node: es.NewExpression) => { + node.arguments = node.arguments.map(x => transform(x)) + return node + } + ], + + [ + 'MethodDefinition', + (node: es.MethodDefinition) => { + node.key = transform(node.key) + node.value = transform(node.value) + return node + } + ], + + [ + 'FunctionExpression', + (node: es.FunctionExpression) => { + if (node.id) { + node.id = transform(node.id) + } + node.params = node.params.map(x => transform(x)) + node.body = transform(node.body) + return node + } + ], + + [ + 'ThisExpression', + (_node: es.ThisExpression) => { + return _node + } + ], + + [ + 'Super', + (_node: es.Super) => { + return _node + } + ], + + [ + 'TryStatement', + (node: es.TryStatement) => { + node.block = transform(node.block) + if (node.handler) { + node.handler = transform(node.handler) + } + if (node.finalizer) { + node.finalizer = transform(node.finalizer) + } + return node + } + ], + [ + 'ThrowStatement', + (node: es.ThrowStatement) => { + node.argument = transform(node.argument) + return node + } + ], + [ + 'SpreadElement', + (node: es.SpreadElement) => { + node.argument = transform(node.argument) + return node + } + ], + [ + 'RestElement', + (node: es.RestElement) => { + node.argument = transform(node.argument) + return node + } + ] +]) + +export function transform(node: NodeType): NodeType { + if (transformers.has(node.type)) { + const transformer = transformers.get(node.type) as (n: NodeType) => NodeType + const transformed = transformer(node) + return transformed + } else { + return node + } +} diff --git a/src/utils/walkers.ts b/src/utils/walkers.ts index dc069b478..d88c38f38 100644 --- a/src/utils/walkers.ts +++ b/src/utils/walkers.ts @@ -3,7 +3,8 @@ acorn.Node differs from estree.Node, so we have this file to handle the `as any` */ import * as walkers from 'acorn-walk' -import { Node } from 'estree' + +import { Node } from '../types' export type FullWalkerCallback = (node: Node, state: TState, type: string) => void type FullAncestorWalkerCallback = ( diff --git a/src/validator/validator.ts b/src/validator/validator.ts index 7892e75f0..e26e9ab75 100644 --- a/src/validator/validator.ts +++ b/src/validator/validator.ts @@ -2,7 +2,7 @@ import * as es from 'estree' import { ConstAssignment } from '../errors/errors' import { NoAssignmentToForVariable } from '../errors/validityErrors' -import { Context, NodeWithInferredType } from '../types' +import { Context, Node, NodeWithInferredType } from '../types' import { getVariableDecarationName } from '../utils/astCreator' import { ancestor, base, FullWalkerCallback } from '../utils/walkers' @@ -15,8 +15,8 @@ export function validateAndAnnotate( program: es.Program, context: Context ): NodeWithInferredType { - const accessedBeforeDeclarationMap = new Map>() - const scopeHasCallExpressionMap = new Map() + const accessedBeforeDeclarationMap = new Map>() + const scopeHasCallExpressionMap = new Map() function processBlock(node: es.Program | es.BlockStatement) { const initialisedIdentifiers = new Map() for (const statement of node.body) { @@ -46,12 +46,12 @@ export function validateAndAnnotate( } // initialise scope of variables - ancestor(program as es.Node, { + ancestor(program as Node, { Program: processBlock, BlockStatement: processBlock, FunctionDeclaration: processFunction, ArrowFunctionExpression: processFunction, - ForStatement(forStatement: es.ForStatement, _ancestors: es.Node[]) { + ForStatement(forStatement: es.ForStatement, _ancestors: Node[]) { const init = forStatement.init! if (init.type === 'VariableDeclaration') { accessedBeforeDeclarationMap.set( @@ -63,9 +63,9 @@ export function validateAndAnnotate( } }) - function validateIdentifier(id: es.Identifier, ancestors: es.Node[]) { + function validateIdentifier(id: es.Identifier, ancestors: Node[]) { const name = id.name - const lastAncestor: es.Node = ancestors[ancestors.length - 2] + const lastAncestor: Node = ancestors[ancestors.length - 2] for (let i = ancestors.length - 1; i >= 0; i--) { const a = ancestors[i] const map = accessedBeforeDeclarationMap.get(a) @@ -95,10 +95,7 @@ export function validateAndAnnotate( ancestor( program, { - VariableDeclaration( - node: NodeWithInferredType, - ancestors: es.Node[] - ) { + VariableDeclaration(node: NodeWithInferredType, ancestors: Node[]) { const lastAncestor = ancestors[ancestors.length - 2] const name = getVariableDecarationName(node) const accessedBeforeDeclaration = accessedBeforeDeclarationMap @@ -107,15 +104,12 @@ export function validateAndAnnotate( node.typability = accessedBeforeDeclaration ? 'Untypable' : 'NotYetTyped' }, Identifier: validateIdentifier, - FunctionDeclaration( - node: NodeWithInferredType, - ancestors: es.Node[] - ) { + FunctionDeclaration(node: NodeWithInferredType, ancestors: Node[]) { // a function declaration can be typed if there are no function calls in the same scope before it const lastAncestor = ancestors[ancestors.length - 2] node.typability = scopeHasCallExpressionMap.get(lastAncestor) ? 'Untypable' : 'NotYetTyped' }, - Pattern(node: es.Pattern, ancestors: es.Node[]) { + Pattern(node: es.Pattern, ancestors: Node[]) { if (node.type === 'Identifier') { validateIdentifier(node, ancestors) } else if (node.type === 'MemberExpression') { @@ -124,7 +118,7 @@ export function validateAndAnnotate( } } }, - CallExpression(call: es.CallExpression, ancestors: es.Node[]) { + CallExpression(call: es.CallExpression, ancestors: Node[]) { for (let i = ancestors.length - 1; i >= 0; i--) { const a = ancestors[i] if (scopeHasCallExpressionMap.has(a)) { diff --git a/src/vm/svml-compiler.ts b/src/vm/svml-compiler.ts index ed791bf4d..2c7adfcf6 100644 --- a/src/vm/svml-compiler.ts +++ b/src/vm/svml-compiler.ts @@ -10,7 +10,7 @@ import { PRIMITIVE_FUNCTION_NAMES, vmPrelude } from '../stdlib/vm.prelude' -import { Context, ContiguousArrayElements } from '../types' +import { Context, ContiguousArrayElements, Node } from '../types' import * as create from '../utils/astCreator' import { recursive, simple } from '../utils/walkers' import OpCodes from './opcodes' @@ -289,7 +289,7 @@ function renameVariables( function recurseBlock( node: es.BlockStatement, inactive: Set, - c: (node: es.Node, state: Set) => void + c: (node: Node, state: Set) => void ) { // get names in current scope const locals = getLocalsInScope(node) @@ -409,7 +409,7 @@ function getLocalsInScope(node: es.BlockStatement | es.Program) { return locals } -function compileArguments(exprs: es.Node[], indexTable: Map[]) { +function compileArguments(exprs: Node[], indexTable: Map[]) { let maxStackSize = 0 for (let i = 0; i < exprs.length; i++) { const { maxStackSize: curExpSize } = compile(exprs[i], indexTable, false) @@ -487,24 +487,24 @@ function compileStatements( // each compiler should return a maxStackSize const compilers = { // wrapper - Program(node: es.Node, indexTable: Map[], insertFlag: boolean) { + Program(node: Node, indexTable: Map[], insertFlag: boolean) { node = node as es.Program return compileStatements(node, indexTable, insertFlag) }, // wrapper - BlockStatement(node: es.Node, indexTable: Map[], insertFlag: boolean) { + BlockStatement(node: Node, indexTable: Map[], insertFlag: boolean) { node = node as es.BlockStatement return compileStatements(node, indexTable, insertFlag) }, // wrapper - ExpressionStatement(node: es.Node, indexTable: Map[], insertFlag: boolean) { + ExpressionStatement(node: Node, indexTable: Map[], insertFlag: boolean) { node = node as es.ExpressionStatement return compile(node.expression, indexTable, insertFlag) }, - IfStatement(node: es.Node, indexTable: Map[], insertFlag: boolean) { + IfStatement(node: Node, indexTable: Map[], insertFlag: boolean) { const { test, consequent, alternate } = node as es.IfStatement const { maxStackSize: m1 } = compile(test, indexTable, false) addUnaryInstruction(OpCodes.BRF, NaN) @@ -541,7 +541,7 @@ const compilers = { ) }, - VariableDeclaration(node: es.Node, indexTable: Map[], insertFlag: boolean) { + VariableDeclaration(node: Node, indexTable: Map[], insertFlag: boolean) { // only supports const / let node = node as es.VariableDeclaration if (node.kind === 'const' || node.kind === 'let') { @@ -567,7 +567,7 @@ const compilers = { }, // handled by insertFlag in compile function - ReturnStatement(node: es.Node, indexTable: Map[], _insertFlag: boolean) { + ReturnStatement(node: Node, indexTable: Map[], _insertFlag: boolean) { node = node as es.ReturnStatement if (loopTracker.length > 0) { throw Error('return not allowed in loops') @@ -580,7 +580,7 @@ const compilers = { // primitive function calls that are predefined, and internal calls. // We differentiate them with callType. CallExpression( - node: es.Node, + node: Node, indexTable: Map[], insertFlag: boolean, isTailCallPosition: boolean = false @@ -627,7 +627,7 @@ const compilers = { return { maxStackSize: Math.max(maxStackOperator, maxStackOperands, 1), insertFlag } }, - UnaryExpression(node: es.Node, indexTable: Map[], insertFlag: boolean) { + UnaryExpression(node: Node, indexTable: Map[], insertFlag: boolean) { node = node as es.UnaryExpression if (VALID_UNARY_OPERATORS.has(node.operator)) { const opCode = VALID_UNARY_OPERATORS.get(node.operator) as number @@ -638,7 +638,7 @@ const compilers = { throw Error('Unsupported operation') }, - BinaryExpression(node: es.Node, indexTable: Map[], insertFlag: boolean) { + BinaryExpression(node: Node, indexTable: Map[], insertFlag: boolean) { node = node as es.BinaryExpression if (VALID_BINARY_OPERATORS.has(node.operator)) { const opCode = VALID_BINARY_OPERATORS.get(node.operator) as number @@ -652,7 +652,7 @@ const compilers = { // convert logical expressions to conditional expressions LogicalExpression( - node: es.Node, + node: Node, indexTable: Map[], insertFlag: boolean, isTailCallPosition: boolean = false @@ -679,7 +679,7 @@ const compilers = { }, ConditionalExpression( - node: es.Node, + node: Node, indexTable: Map[], insertFlag: boolean, isTailCallPosition: boolean = false @@ -703,7 +703,7 @@ const compilers = { return { maxStackSize, insertFlag: false } }, - ArrowFunctionExpression(node: es.Node, indexTable: Map[], insertFlag: boolean) { + ArrowFunctionExpression(node: Node, indexTable: Map[], insertFlag: boolean) { node = node as es.ArrowFunctionExpression // node.body is either a block statement or a single node to return const bodyNode = @@ -729,7 +729,7 @@ const compilers = { return { maxStackSize: 1, insertFlag } }, - Identifier(node: es.Node, indexTable: Map[], insertFlag: boolean) { + Identifier(node: Node, indexTable: Map[], insertFlag: boolean) { node = node as es.Identifier let envLevel @@ -765,7 +765,7 @@ const compilers = { }, // string, boolean, number or null - Literal(node: es.Node, indexTable: Map[], insertFlag: boolean) { + Literal(node: Node, indexTable: Map[], insertFlag: boolean) { node = node as es.Literal const value = node.value if (value === null) { @@ -798,7 +798,7 @@ const compilers = { }, // array declarations - ArrayExpression(node: es.Node, indexTable: Map[], insertFlag: boolean) { + ArrayExpression(node: Node, indexTable: Map[], insertFlag: boolean) { node = node as es.ArrayExpression addNullaryInstruction(OpCodes.NEWA) const elements = node.elements as ContiguousArrayElements @@ -819,7 +819,7 @@ const compilers = { return { maxStackSize, insertFlag } }, - AssignmentExpression(node: es.Node, indexTable: Map[], insertFlag: boolean) { + AssignmentExpression(node: Node, indexTable: Map[], insertFlag: boolean) { node = node as es.AssignmentExpression if (node.left.type === 'Identifier') { const { envLevel, index, isVar } = indexOf(indexTable, node.left) @@ -847,12 +847,12 @@ const compilers = { throw Error('Invalid Assignment') }, - ForStatement(_node: es.Node, _indexTable: Map[], _insertFlag: boolean) { + ForStatement(_node: Node, _indexTable: Map[], _insertFlag: boolean) { throw Error('Unsupported operation') }, // Loops need to have their own environment due to closures - WhileStatement(node: es.Node, indexTable: Map[], insertFlag: boolean) { + WhileStatement(node: Node, indexTable: Map[], insertFlag: boolean) { node = node as taggedWhileStatement const isFor = (node as taggedWhileStatement).isFor const condIndex = functionCode.length @@ -889,7 +889,7 @@ const compilers = { return { maxStackSize: Math.max(m1, m2), insertFlag } }, - BreakStatement(node: es.Node, indexTable: Map[], insertFlag: boolean) { + BreakStatement(node: Node, indexTable: Map[], insertFlag: boolean) { // keep track of break instruction addNullaryInstruction(OpCodes.POPENV) loopTracker[loopTracker.length - 1][BREAK_INDEX].push(functionCode.length) @@ -897,7 +897,7 @@ const compilers = { return { maxStackSize: 0, insertFlag } }, - ContinueStatement(node: es.Node, indexTable: Map[], insertFlag: boolean) { + ContinueStatement(node: Node, indexTable: Map[], insertFlag: boolean) { // keep track of continue instruction // no need to POPENV as continue will go to the end of the while loop loopTracker[loopTracker.length - 1][CONT_INDEX].push(functionCode.length) @@ -905,11 +905,11 @@ const compilers = { return { maxStackSize: 0, insertFlag } }, - ObjectExpression(_node: es.Node, _indexTable: Map[], _insertFlag: boolean) { + ObjectExpression(_node: Node, _indexTable: Map[], _insertFlag: boolean) { throw Error('Unsupported operation') }, - MemberExpression(node: es.Node, indexTable: Map[], insertFlag: boolean) { + MemberExpression(node: Node, indexTable: Map[], insertFlag: boolean) { node = node as es.MemberExpression if (node.computed) { const { maxStackSize: m1 } = compile(node.object, indexTable, false) @@ -921,17 +921,17 @@ const compilers = { throw Error('Unsupported operation') }, - Property(_node: es.Node, _indexTable: Map[], _insertFlag: boolean) { + Property(_node: Node, _indexTable: Map[], _insertFlag: boolean) { throw Error('Unsupported operation') }, - DebuggerStatement(_node: es.Node, _indexTable: Map[], _insertFlag: boolean) { + DebuggerStatement(_node: Node, _indexTable: Map[], _insertFlag: boolean) { throw Error('Unsupported operation') } } function compile( - expr: es.Node, + expr: Node, indexTable: Map[], insertFlag: boolean, isTailCallPosition: boolean = false