UNPKG

js-slang

Version:

Javascript-based implementations of Source, written in Typescript

171 lines 9.85 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.StepperBlockExpression = void 0; const __1 = require(".."); const generator_1 = require("../../generator"); const __2 = require("../.."); const utils_1 = require("../../utils"); // TODO: add docs, because this is a block expression, not a block statement, and this does not follow official estree spec class StepperBlockExpression { constructor(body, innerComments, leadingComments, trailingComments, loc, range) { this.type = 'BlockStatement'; this.body = body; this.innerComments = innerComments; this.leadingComments = leadingComments; this.trailingComments = trailingComments; this.loc = loc; this.range = range; } static create(node) { return new StepperBlockExpression(node.body.map(ast => (0, generator_1.convert)(ast))); } isContractible() { return (this.body.length === 0 || (this.body.length === 1 && !this.body[0].isOneStepPossible()) || // { 1; } -> undefined; this.body[0].type === 'ReturnStatement'); } isOneStepPossible() { return this.isContractible() || this.body[0].isOneStepPossible() || this.body.length >= 2; } contract() { if (this.body.length === 0 || (this.body.length === 1 && !this.body[0].isOneStepPossible())) { __2.redex.preRedex = [this]; __2.redex.postRedex = []; return __1.undefinedNode; } if (this.body[0].type === 'ReturnStatement') { const returnStmt = this.body[0]; returnStmt.contract(); return returnStmt.argument || __1.undefinedNode; } throw new Error('Cannot contract block expression ' + JSON.stringify(this.isContractible())); } oneStep() { if (this.isContractible()) { return this.contract(); } // reduce the first statement if (this.body[0].isOneStepPossible()) { const firstStatementOneStep = this.body[0].oneStep(); const afterSubstitutedScope = this.body.slice(1); if (firstStatementOneStep === __1.undefinedNode) { return new StepperBlockExpression([afterSubstitutedScope].flat(), this.innerComments, this.leadingComments, this.trailingComments, this.loc, this.range); } return new StepperBlockExpression([firstStatementOneStep, ...afterSubstitutedScope], this.innerComments, this.leadingComments, this.trailingComments, this.loc, this.range); } // If the first statement is constant declaration, gracefully handle it! if (this.body[0].type == 'VariableDeclaration') { const declarations = (0, utils_1.assignMuTerms)(this.body[0].declarations); const afterSubstitutedScope = this.body .slice(1) .map(current => declarations .filter(declarator => declarator.init) .reduce((statement, declarator) => statement.substitute(declarator.id, declarator.init), current)); const substitutedProgram = new StepperBlockExpression(afterSubstitutedScope, this.innerComments, this.leadingComments, this.trailingComments, this.loc, this.range); __2.redex.preRedex = [this.body[0]]; __2.redex.postRedex = declarations.map(x => x.id); return substitutedProgram; } // If the first statement is function declaration, also gracefully handle it! if (this.body[0].type == 'FunctionDeclaration') { const arrowFunction = this.body[0].getArrowFunctionExpression(); const functionIdentifier = this.body[0].id; const afterSubstitutedScope = this.body .slice(1) .map(statement => statement.substitute(functionIdentifier, arrowFunction)); const substitutedProgram = new StepperBlockExpression(afterSubstitutedScope, this.innerComments, this.leadingComments, this.trailingComments, this.loc, this.range); __2.redex.preRedex = [this.body[0]]; __2.redex.postRedex = afterSubstitutedScope; return substitutedProgram; } const firstValueStatement = this.body[0]; // After this stage, the first statement is a value statement. Now, proceed until getting the second value statement. // if the second statement is return statement, remove the first statement if (this.body.length >= 2 && this.body[1].type == 'ReturnStatement') { __2.redex.preRedex = [this.body[0]]; const afterSubstitutedScope = this.body.slice(1); __2.redex.postRedex = []; return new StepperBlockExpression(afterSubstitutedScope, this.innerComments, this.leadingComments, this.trailingComments, this.loc, this.range); } if (this.body.length >= 2 && this.body[1].isOneStepPossible()) { const secondStatementOneStep = this.body[1].oneStep(); const afterSubstitutedScope = this.body.slice(2); if (secondStatementOneStep === __1.undefinedNode) { return new StepperBlockExpression([firstValueStatement, afterSubstitutedScope].flat(), this.innerComments, this.leadingComments, this.trailingComments, this.loc, this.range); } return new StepperBlockExpression([ firstValueStatement, secondStatementOneStep, afterSubstitutedScope ].flat(), this.innerComments, this.leadingComments, this.trailingComments, this.loc, this.range); } // If the second statement is constant declaration, gracefully handle it! if (this.body.length >= 2 && this.body[1].type == 'VariableDeclaration') { const declarations = (0, utils_1.assignMuTerms)(this.body[1].declarations); const afterSubstitutedScope = this.body .slice(2) .map(current => declarations .filter(declarator => declarator.init) .reduce((statement, declarator) => statement.substitute(declarator.id, declarator.init), current)); const substitutedProgram = new StepperBlockExpression([firstValueStatement, afterSubstitutedScope].flat(), this.innerComments, this.leadingComments, this.trailingComments, this.loc, this.range); __2.redex.preRedex = [this.body[1]]; __2.redex.postRedex = declarations.map(x => x.id); return substitutedProgram; } // If the second statement is function declaration, also gracefully handle it! if (this.body.length >= 2 && this.body[1].type == 'FunctionDeclaration') { const arrowFunction = this.body[1].getArrowFunctionExpression(); const functionIdentifier = this.body[1].id; const afterSubstitutedScope = this.body .slice(2) .map(statement => statement.substitute(functionIdentifier, arrowFunction)); const substitutedProgram = new StepperBlockExpression([firstValueStatement, afterSubstitutedScope].flat(), this.innerComments, this.leadingComments, this.trailingComments, this.loc, this.range); __2.redex.preRedex = [this.body[1]]; __2.redex.postRedex = afterSubstitutedScope; return substitutedProgram; } // After this stage, we have two value inducing statement. Remove the first one. this.body[0].contractEmpty(); // update the contracted statement onto redex return new StepperBlockExpression(this.body.slice(1), this.innerComments, this.leadingComments, this.trailingComments, this.loc, this.range); } substitute(id, value) { const valueFreeNames = value.freeNames(); const scopeNames = this.scanAllDeclarationNames(); const repeatedNames = valueFreeNames.filter(name => scopeNames.includes(name)); let protectedNamesSet = new Set(this.allNames()); repeatedNames.forEach(name => protectedNamesSet.delete(name)); const protectedNames = Array.from(protectedNamesSet); const newNames = (0, utils_1.getFreshName)(repeatedNames, protectedNames); const currentBlockExpression = newNames.reduce((current, name, index) => current.rename(repeatedNames[index], name), this); if (currentBlockExpression.scanAllDeclarationNames().includes(id.name)) { return currentBlockExpression; } return new StepperBlockExpression(currentBlockExpression.body.map(statement => statement.substitute(id, value)), currentBlockExpression.innerComments, currentBlockExpression.leadingComments, currentBlockExpression.trailingComments, currentBlockExpression.loc, currentBlockExpression.range); } scanAllDeclarationNames() { return this.body .filter(ast => ast.type === 'VariableDeclaration' || ast.type === 'FunctionDeclaration') .flatMap((ast) => { if (ast.type === 'VariableDeclaration') { return ast.declarations.map(ast => ast.id.name); } else { // Function Declaration return [ast.id.name]; } }); } freeNames() { const names = new Set(this.body.flatMap(ast => ast.freeNames())); this.scanAllDeclarationNames().forEach(name => names.delete(name)); return Array.from(names); } allNames() { return Array.from(new Set(this.body.flatMap(ast => ast.allNames()))); } rename(before, after) { return new StepperBlockExpression(this.body.map(statement => statement.rename(before, after)), this.innerComments, this.leadingComments, this.trailingComments, this.loc, this.range); } } exports.StepperBlockExpression = StepperBlockExpression; //# sourceMappingURL=BlockExpression.js.map