js-slang
Version:
Javascript-based implementations of Source, written in Typescript
125 lines • 6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.StepperFunctionApplication = void 0;
const astring = require("astring");
const __1 = require("../..");
const generator_1 = require("../../generator");
const BlockStatement_1 = require("../Statement/BlockStatement");
const builtins_1 = require("../../builtins");
const BlockExpression_1 = require("./BlockExpression");
class StepperFunctionApplication {
constructor(callee, args, optional = false, leadingComments, trailingComments, loc, range) {
this.type = 'CallExpression';
this.callee = callee;
this.arguments = args;
this.optional = optional;
this.leadingComments = leadingComments;
this.trailingComments = trailingComments;
this.loc = loc;
this.range = range;
}
static create(node) {
return new StepperFunctionApplication((0, generator_1.convert)(node.callee), node.arguments.map(arg => (0, generator_1.convert)(arg)), node.optional, node.leadingComments, node.trailingComments, node.loc, node.range);
}
isContractible() {
const isValidCallee = this.callee.type === 'ArrowFunctionExpression' ||
(this.callee.type === 'Identifier' && (0, builtins_1.isBuiltinFunction)(this.callee.name));
if (!isValidCallee) {
// Since the callee can not be proceed further, calling non callables should result to an error.
if (!this.callee.isOneStepPossible() &&
this.arguments.every(arg => !arg.isOneStepPossible())) {
throw new Error(`Line ${this.loc?.start.line || 0}: Calling non-function value ${astring.generate(this.callee)}`);
}
return false;
}
if (this.callee.type === 'ArrowFunctionExpression') {
const arrowFunction = this.callee;
if (arrowFunction.params.length !== this.arguments.length) {
throw new Error(`Line ${this.loc?.start.line || 0}: Expected ${arrowFunction.params.length} arguments, but got ${this.arguments.length}.`);
}
}
return this.arguments.every(arg => !arg.isOneStepPossible());
}
isOneStepPossible() {
if (this.isContractible())
return true;
if (this.callee.isOneStepPossible())
return true;
return this.arguments.some(arg => arg.isOneStepPossible());
}
contract() {
__1.redex.preRedex = [this];
if (!this.isContractible())
throw new Error();
if (this.callee.type === 'Identifier') {
const functionName = this.callee.name;
if ((0, builtins_1.isBuiltinFunction)(functionName)) {
const result = (0, builtins_1.getBuiltinFunction)(functionName, this.arguments);
__1.redex.postRedex = [result];
return result;
}
throw new Error(`Unknown builtin function: ${functionName}`);
}
if (this.callee.type !== 'ArrowFunctionExpression') {
throw new Error();
}
const lambda = this.callee;
const args = this.arguments;
let result = lambda.body;
if (result instanceof BlockStatement_1.StepperBlockStatement) {
const blockStatement = lambda.body;
if (blockStatement.body.length === 0) {
result = new BlockExpression_1.StepperBlockExpression([]);
}
else if (blockStatement.body[0].type === 'ReturnStatement') {
// (x => {return 2 + 3;})(3) -> 2 + 3;
result = blockStatement.body[0].argument;
}
else {
result = new BlockExpression_1.StepperBlockExpression(blockStatement.body);
}
}
else {
result = lambda.body;
}
if (lambda.name && !this.callee.scanAllDeclarationNames().includes(lambda.name)) {
result = result.substitute({ type: 'Identifier', name: lambda.name }, lambda);
}
lambda.params.forEach((param, i) => {
result = result.substitute(param, args[i]);
});
__1.redex.postRedex = [result];
return result;
}
oneStep() {
if (this.isContractible()) {
// @ts-expect-error: contract can return StepperBlockExpression but it's handled at runtime
return this.contract();
}
if (this.callee.isOneStepPossible()) {
return new StepperFunctionApplication(this.callee.oneStep(), this.arguments, this.optional, this.leadingComments, this.trailingComments, this.loc, this.range);
}
for (let i = 0; i < this.arguments.length; i++) {
if (this.arguments[i].isOneStepPossible()) {
const newArgs = [...this.arguments];
newArgs[i] = this.arguments[i].oneStep();
return new StepperFunctionApplication(this.callee, newArgs, this.optional, this.leadingComments, this.trailingComments, this.loc, this.range);
}
}
throw new Error('No one step possible');
}
substitute(id, value) {
return new StepperFunctionApplication(this.callee.substitute(id, value), this.arguments.map(arg => arg.substitute(id, value)), this.optional, this.leadingComments, this.trailingComments, this.loc, this.range);
}
freeNames() {
return Array.from(new Set([...this.callee.freeNames(), ...this.arguments.flatMap(arg => arg.freeNames())]));
}
allNames() {
return Array.from(new Set([...this.callee.allNames(), ...this.arguments.flatMap(arg => arg.allNames())]));
}
rename(before, after) {
return new StepperFunctionApplication(this.callee.rename(before, after), this.arguments.map(arg => arg.rename(before, after)), this.optional, this.leadingComments, this.trailingComments, this.loc, this.range);
}
}
exports.StepperFunctionApplication = StepperFunctionApplication;
//# sourceMappingURL=FunctionApplication.js.map