UNPKG

js-slang

Version:

Javascript-based implementations of Source, written in Typescript

209 lines 9.92 kB
"use strict"; /* 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