js-slang
Version:
Javascript-based implementations of Source, written in Typescript
209 lines • 9.92 kB
JavaScript
/*
TODO: Write docs
convert estree into corresponding stepper type
Every class should have the following properties
- basic StepperBaseNodeInterface
- constructor: create new AST from class type StepperBaseNode
- static create: factory method to parse estree to StepperAST
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.explain = exports.convert = void 0;
const astring_1 = require("astring");
const BinaryExpression_1 = require("./nodes/Expression/BinaryExpression");
const UnaryExpression_1 = require("./nodes/Expression/UnaryExpression");
const Literal_1 = require("./nodes/Expression/Literal");
const ExpressionStatement_1 = require("./nodes/Statement/ExpressionStatement");
const Program_1 = require("./nodes/Program");
const VariableDeclaration_1 = require("./nodes/Statement/VariableDeclaration");
const Identifier_1 = require("./nodes/Expression/Identifier");
const BlockStatement_1 = require("./nodes/Statement/BlockStatement");
const IfStatement_1 = require("./nodes/Statement/IfStatement");
const ConditionalExpression_1 = require("./nodes/Expression/ConditionalExpression");
const ArrowFunctionExpression_1 = require("./nodes/Expression/ArrowFunctionExpression");
const FunctionApplication_1 = require("./nodes/Expression/FunctionApplication");
const ReturnStatement_1 = require("./nodes/Statement/ReturnStatement");
const FunctionDeclaration_1 = require("./nodes/Statement/FunctionDeclaration");
const ArrayExpression_1 = require("./nodes/Expression/ArrayExpression");
const LogicalExpression_1 = require("./nodes/Expression/LogicalExpression");
const builtins_1 = require("./builtins");
const undefinedNode = new Literal_1.StepperLiteral('undefined');
const nodeConverters = {
Literal: (node) => Literal_1.StepperLiteral.create(node),
UnaryExpression: (node) => UnaryExpression_1.StepperUnaryExpression.create(node),
BinaryExpression: (node) => BinaryExpression_1.StepperBinaryExpression.create(node),
LogicalExpression: (node) => LogicalExpression_1.StepperLogicalExpression.create(node),
FunctionDeclaration: (node) => FunctionDeclaration_1.StepperFunctionDeclaration.create(node),
ExpressionStatement: (node) => ExpressionStatement_1.StepperExpressionStatement.create(node),
ConditionalExpression: (node) => ConditionalExpression_1.StepperConditionalExpression.create(node),
ArrowFunctionExpression: (node) => ArrowFunctionExpression_1.StepperArrowFunctionExpression.create(node),
ArrayExpression: (node) => ArrayExpression_1.StepperArrayExpression.create(node),
CallExpression: (node) => FunctionApplication_1.StepperFunctionApplication.create(node),
ReturnStatement: (node) => ReturnStatement_1.StepperReturnStatement.create(node),
Program: (node) => Program_1.StepperProgram.create(node),
VariableDeclaration: (node) => VariableDeclaration_1.StepperVariableDeclaration.create(node),
VariableDeclarator: (node) => VariableDeclaration_1.StepperVariableDeclarator.create(node),
Identifier: (node) => {
if (node.name === 'NaN') {
return new Literal_1.StepperLiteral(NaN, 'NaN');
}
else if (node.name === 'Infinity') {
return new Literal_1.StepperLiteral(Infinity, 'Infinity');
}
else {
return Identifier_1.StepperIdentifier.create(node);
}
},
BlockStatement: (node) => BlockStatement_1.StepperBlockStatement.create(node),
IfStatement: (node) => IfStatement_1.StepperIfStatement.create(node)
};
function convert(node) {
const converter = nodeConverters[node.type];
return converter ? converter(node) : undefinedNode;
}
exports.convert = convert;
// Explanation generator
function explain(redex) {
const explainers = {
UnaryExpression: (node) => {
if (node.operator === '-') {
return ('Unary expression evaluated, value ' +
JSON.stringify(node.argument.value) +
' negated.');
}
else if (node.operator === '!') {
return ('Unary expression evaluated, boolean ' +
JSON.stringify(node.argument.value) +
' negated.');
}
else {
throw new Error('Unsupported unary operator ' + node.operator);
}
},
BinaryExpression: (node) => {
return 'Binary expression ' + (0, astring_1.generate)(node) + ' evaluated';
},
LogicalExpression: (node) => {
if (node.operator == '&&') {
return node.left.value === false
? 'AND operation evaluated, left of operator is true, continue evaluating right of operator'
: 'AND operation evaluated, left of operator is false, stop evaluation';
}
else if (node.operator == '||') {
return node.left.value === true
? 'OR operation evaluated, left of operator is true, stop evaluation'
: 'OR operation evaluated, left of operator is false, continue evaluating right of operator';
}
else {
throw new Error('Invalid operator');
}
},
VariableDeclaration: (node) => {
if (node.kind === 'const') {
return ('Constant ' +
node.declarations.map(ast => ast.id.name).join(', ') +
' declared and substituted into the rest of block');
}
else {
return '...';
}
},
ReturnStatement: (node) => {
if (!node.argument) {
throw new Error('return argument should not be empty');
}
return (0, astring_1.generate)(node.argument) + ' returned';
},
FunctionDeclaration: (node) => {
return `Function ${node.id.name} declared, parameter(s) ${node.params.map(x => (0, astring_1.generate)(x))} required`;
},
ExpressionStatement: (node) => {
return (0, astring_1.generate)(node.expression) + ' finished evaluating';
},
BlockStatement: (node) => {
if (node.body.length === 0) {
return 'Empty block expression evaluated';
}
return (0, astring_1.generate)(node.body[0]) + ' finished evaluating';
},
BlockExpression: (_node) => {
throw new Error('Not implemented');
},
ConditionalExpression: (node) => {
const test = node.test; // test should have typeof literal
if (test.type !== 'Literal') {
throw new Error('Invalid conditional contraction. `test` should be literal.');
}
const testStatus = test.value;
if (typeof testStatus !== 'boolean') {
throw new Error('Invalid conditional contraction. `test` should be boolean, got ' +
typeof testStatus +
' instead.');
}
if (testStatus === true) {
return 'Conditional expression evaluated, condition is true, consequent evaluated';
}
else {
return 'Conditional expression evaluated, condition is false, alternate evaluated';
}
},
CallExpression: (node) => {
if (node.callee.type !== 'ArrowFunctionExpression' && node.callee.type !== 'Identifier') {
throw new Error('`callee` should be function expression.');
}
// Determine whether the called function is built-in or not and create explanation accordingly
const func = node.callee;
if (func.name && (0, builtins_1.isBuiltinFunction)(func.name)) {
return `${func.name} runs`;
// @ts-ignore func.body.type can be StepperBlockExpression
}
else if (func.body.type === 'BlockStatement') {
if (func.params.length === 0) {
return '() => {...}' + ' runs';
}
const paramDisplay = func.params.map(x => x.name).join(', ');
const argDisplay = node.arguments
.map(x => (x.type === 'ArrowFunctionExpression' || x.type === 'Identifier') &&
x.name !== undefined
? x.name
: (0, astring_1.generate)(x))
.join(', ');
return 'Function ' + func.name + ' takes in ' + argDisplay + ' as input ' + paramDisplay;
}
else {
if (func.params.length === 0) {
return (0, astring_1.generate)(func) + ' runs';
}
return (node.arguments.map(x => (0, astring_1.generate)(x)).join(', ') +
' substituted into ' +
func.params.map(x => x.name).join(', ') +
' of ' +
(0, astring_1.generate)(func));
}
},
ArrowFunctionExpression: (_node) => {
throw new Error('Not implemented.');
},
IfStatement: (node) => {
if (node.test instanceof Literal_1.StepperLiteral) {
if (node.test.value) {
return 'If statement evaluated, condition true, proceed to if block';
}
else {
return 'If statement evaluated, condition false, proceed to else block';
}
}
else {
throw new Error('Not implemented');
}
},
Default: (_) => {
return '...';
}
};
//@ts-ignore gracefully handle default ast node
const explainer = explainers[redex.type] ?? explainers.Default;
return explainer(redex);
}
exports.explain = explain;
//# sourceMappingURL=generator.js.map
;