js-slang
Version:
Javascript-based implementations of Source, written in Typescript
144 lines • 6.89 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.StepperProgram = void 0;
const generator_1 = require("../generator");
const __1 = require("..");
const utils_1 = require("../utils");
const _1 = require(".");
class StepperProgram {
isContractible() {
return false;
}
isOneStepPossible() {
return this.body.length === 0
? false // unlike BlockStatement
: this.body[0].isOneStepPossible() ||
this.body.length >= 2 ||
(this.body.length == 1 &&
(this.body[0].type == 'VariableDeclaration' ||
this.body[0].type == 'FunctionDeclaration'));
}
contract() {
throw new Error('not implemented');
}
oneStep() {
// 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 StepperProgram([afterSubstitutedScope].flat());
}
return new StepperProgram([firstStatementOneStep, afterSubstitutedScope].flat());
}
// 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); // for arrow function expression
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 StepperProgram(afterSubstitutedScope);
__1.redex.preRedex = [this.body[0]];
__1.redex.postRedex = afterSubstitutedScope;
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 StepperProgram(afterSubstitutedScope);
__1.redex.preRedex = [this.body[0]];
__1.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 (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 StepperProgram([firstValueStatement, afterSubstitutedScope].flat());
}
return new StepperProgram([
firstValueStatement,
secondStatementOneStep,
afterSubstitutedScope
].flat());
}
// 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 StepperProgram([firstValueStatement, afterSubstitutedScope].flat());
__1.redex.preRedex = [this.body[1]];
__1.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 StepperProgram([firstValueStatement, afterSubstitutedScope].flat());
__1.redex.preRedex = [this.body[1]];
__1.redex.postRedex = afterSubstitutedScope;
return substitutedProgram;
}
this.body[0].contractEmpty(); // update the contracted statement onto redex
return new StepperProgram(this.body.slice(1));
}
static create(node) {
return new StepperProgram(node.body.map(ast => (0, generator_1.convert)(ast)), node.comments, node.leadingComments, node.trailingComments, node.loc, node.range);
}
constructor(body, // TODO: Add support for variable declaration
comments, leadingComments, trailingComments, loc, range) {
this.type = 'Program';
this.sourceType = 'module';
this.body = body;
this.comments = comments;
this.leadingComments = leadingComments;
this.trailingComments = trailingComments;
this.loc = loc;
this.range = range;
}
substitute(id, value) {
return new StepperProgram(this.body.map(statement => statement.substitute(id, value)));
}
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 StepperProgram(this.body.map(statement => statement.rename(before, after)));
}
}
exports.StepperProgram = StepperProgram;
//# sourceMappingURL=Program.js.map