UNPKG

js-slang

Version:

Javascript-based implementations of Source, written in Typescript

182 lines 9.91 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.StepperBlockStatement = void 0; const __1 = require(".."); const generator_1 = require("../../generator"); const __2 = require("../.."); const utils_1 = require("../../utils"); class StepperBlockStatement { 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 StepperBlockStatement(node.body.map(ast => (0, generator_1.convert)(ast)), node.innerComments, node.leadingComments, node.trailingComments, node.loc, node.range); } isContractible() { return this.body.length === 0 || (this.body.length === 1 && !this.body[0].isContractible()); } isOneStepPossible() { return true; } contract() { if (this.body.length === 0) { __2.redex.preRedex = [this]; __2.redex.postRedex = []; return __1.undefinedNode; } if (this.body.length === 1) { __2.redex.preRedex = [this]; __2.redex.postRedex = [this.body[0]]; return this.body[0]; } throw new Error('Not implemented'); } contractEmpty() { __2.redex.preRedex = [this]; __2.redex.postRedex = []; } oneStep() { if (this.isContractible()) { return this.contract(); } if (this.body[0].type === 'ReturnStatement') { const returnStmt = this.body[0]; __2.redex.preRedex = [this]; __2.redex.postRedex = [returnStmt]; return returnStmt; } // 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 StepperBlockStatement([afterSubstitutedScope].flat(), this.innerComments, this.leadingComments, this.trailingComments, this.loc, this.range); } return new StepperBlockStatement([firstStatementOneStep, afterSubstitutedScope].flat(), 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 StepperBlockStatement(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 StepperBlockStatement(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 StepperBlockStatement(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 StepperBlockStatement([firstValueStatement, afterSubstitutedScope].flat(), this.innerComments, this.leadingComments, this.trailingComments, this.loc, this.range); } return new StepperBlockStatement([ 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 StepperBlockStatement([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 StepperBlockStatement([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 StepperBlockStatement(this.body.slice(1), this.innerComments, this.leadingComments, this.trailingComments, this.loc, this.range); } substitute(id, value, upperBoundName) { // Alpha renaming // Check whether should be renamed // Renaming stage should not be counted as one step. const valueFreeNames = value.freeNames(); const scopeNames = this.scanAllDeclarationNames(); const repeatedNames = valueFreeNames.filter(name => scopeNames.includes(name)); let protectedNamesSet = new Set([this.allNames(), upperBoundName ?? []].flat()); repeatedNames.forEach(name => protectedNamesSet.delete(name)); const protectedNames = Array.from(protectedNamesSet); const newNames = (0, utils_1.getFreshName)(repeatedNames, protectedNames); const currentBlockStatement = newNames.reduce((current, name, index) => current.rename(repeatedNames[index], name), this); if (currentBlockStatement.scanAllDeclarationNames().includes(id.name)) { // DO nothing return currentBlockStatement; } return new StepperBlockStatement(currentBlockStatement.body.map(statement => statement.substitute(id, value)), this.innerComments, this.leadingComments, this.trailingComments, this.loc, this.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 StepperBlockStatement(this.body.map(statement => statement.rename(before, after)), this.innerComments, this.leadingComments, this.trailingComments, this.loc, this.range); } } exports.StepperBlockStatement = StepperBlockStatement; //# sourceMappingURL=BlockStatement.js.map