The article you linked contains a crazy approach that actally works: during each eval()
call, we create a new closure inside that eval
scope and export it so that to we can use it evaluate the next statement.
var __EVAL = s => eval(`void (__EVAL = ${__EVAL.toString()}); ${s}`);
function evaluate(expr) {
try {
const result = __EVAL(expr);
console.log(expr, '===>', result)
} catch(err) {
console.log(expr, 'ERROR:', err.message)
}
}
evaluate('var ten = 10')
evaluate('function cube(x) { return x ** 3 }')
evaluate('ten + cube(3)')
evaluate('console.log("SIDE EFFECT")')
evaluate('let twenty = 20')
evaluate('twenty + 40') // NO PROBLEM :D