I'm creating a custom JavaScript console that I expect to work exactly like the console in dev tools. (or … something like a REPL) https://github.com/MohammadMD1383/js-interactive
I get user inputs one by one and evaluate them. eval(userInput)
the problem is with defining variables. I noticed that the eval
function uses a new VM each time, so the declaration is in a separate VM than a call to the variable. so it causes the error someVarName is not defined
the sample of my code:
button.onclick = () => {
evaluateMyExpression(textarea.value);
};
function evaluateMyExpression(code) {
let result = eval(code);
// do something else …
}
You can use a generator function and "feed" expressions to it as they come in. Since the generator retains its context, all variables from previous lines will be there when you evaluate the next one:
function* VM(expr) {
while (1) {
try {
expr = yield eval(expr || '')
} catch(err) {
expr = yield err
}
}
}
vm = VM()
vm.next()
function evaluate(line) {
let result = vm.next(line).value
if (result instanceof Error)
console.log(line, 'ERROR', result.message)
else
console.log(line, 'result', result)
}
evaluate('var a = 55')
evaluate('a')
evaluate('var b = 5')
evaluate('c = a + b')
evaluate('foobar--')
evaluate('c++')
evaluate('[a, b, c]')
As suggested in another thread, a "self-similar" eval
will also preserve block-scoping variables:
let _EVAL = e => eval(`_EVAL=${_EVAL}; undefined; ${e}`)
function evaluate(line) {
try {
let result = _EVAL(line)
console.log(line, '==>', result)
} catch (err) {
console.log(line, '==>', 'ERROR', err.message)
}
}
evaluate('var a = 55')
evaluate('a')
evaluate('foobar--')
evaluate('let x = 10')
evaluate('const y = 20')
evaluate('[a, x, y]')