js-slang
Version:
Javascript-based implementations of Source, written in Typescript
182 lines • 9.91 kB
JavaScript
"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