const fs = require("fs");
const deb = require('../src/deb.js');
const escodegen = require("escodegen");
const espree = require("espree");
const estraverse = require("estraverse");
const { Console } = require("console");
"use strict";
module.exports = constantFolding;
/**
* A function that fold constants
* @param {string} code A string with the input code
* @param {bool} debug Activates debug mode (print AST)
* @returns {string} A string with the new code with constant folded
*/
function constantFolding(code, debug = false) {
const ast = espree.parse(code, { ecmaVersion: 6, loc: false });
estraverse.traverse(ast, {
leave: function (node, parent) {
if (
node.type == "BinaryExpression" &&
node.left.type == "Literal" && node.right.type == "Literal"
) {
replaceByLiteral(node);
} else if (
node.type === "CallExpression" &&
node.callee.type === "MemberExpression" &&
node.callee.object.type === "ArrayExpression" &&
node.callee.property.name === "concat") {
replaceByArray(node);
}
else if (
node.type === "MemberExpression" &&
node.object.type === "ArrayExpression" &&
node.property.name === "length") {
replaceByNumber(node);
}
else if (
node.type === "CallExpression" &&
node.callee.type === "MemberExpression" &&
node.callee.object.type === "ArrayExpression" &&
node.callee.property.name === "pop") {
popReplace(node);
}
},
});
if (debug) { deb(ast); };
let newCode = escodegen.generate(ast);
return newCode;
}
/**
* A function that replace a binary expresion node to a literal node
* @param {object} node Node of AST
*/
function replaceByLiteral(node) {
node.type = "Literal";
node.value = eval(`${node.left.raw} ${node.operator} ${node.right.raw}`);
node.raw = String(node.value);
delete node.left;
delete node.right;
}
/**
* Replace concat member by the result array
* @param {object} node Node of AST
*/
function replaceByArray(node) {
node.type = "ArrayExpression";
let result = node.callee.object.elements;
for (let argumentNode of node.arguments) {
if (argumentNode.type === 'ArrayExpression') {
result = result.concat(argumentNode.elements)
} else if (argumentNode.type === 'Literal') {
result.push(argumentNode);
}
}
node.elements = result;
delete node.callee;
delete node.arguments;
delete node.optional;
}
/**
* Replace a length member by the result number
* @param {object} node Node of AST
*/
function replaceByNumber(node) {
node.type = "Literal";
node.value = node.object.elements.length;
node.raw = String(node.value);
delete node.object;
delete node.property;
}
/**
* Replaces an array by the last element
* @param {object} node Node of AST
*/
function popReplace(node) {
let newNode = node.callee.object.elements.pop();
delete node.type;
delete node.callee;
delete node.arguments;
delete node.optional;
for (let key in newNode) {
node[key] = newNode[key];
}
}