We're building a small REPL that evaluates (with eval) javascript expressions as they are being entered by the user. Since the whole thing is event-driven, evaluation must take place in a separate function, but the context (that is, all declared variables and functions) must be preserved between the calls. I came up with the following solution:
function* _EVAL(s) {
while (1) {
try {
s = yield eval(s)
} catch(err) {
s = yield err
}
}
}
let _eval = _EVAL()
_eval.next()
function evaluate(expr) {
let result = _eval.next(expr).value
if (result instanceof Error)
console.log(expr, 'ERROR:', result.message)
else
console.log(expr, '===>', result)
}
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') // PROBLEMAs you can see it works fine with function-scoped variables (var and function), but fails on block scoped ones (let).
How can I write a context-preserving eval wrapper that would also preserve block-scoped variables?
The code runs in a browser, DOM and Workers are fully available.
It should be mentioned that the desired function must handle side effects properly, that is, each line of code, or, at least, each side effect, should be performed exactly once.
Links:
JavaScript: do all evaluations in one vm | https://vane.life/2016/04/03/eval-locally-with-persistent-context/
from Context-preserving eval
No comments:
Post a Comment