保持上下文的评估 [英] Context-preserving eval

查看:40
本文介绍了保持上下文的评估的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们正在构建一个小的REPL,用于评估(由 eval )用户所输入的javascript表达式.由于整个过程都是事件驱动的,因此评估必须在单独的函数中进行,但是必须在调用之间保留上下文(即所有声明的变量和函数).我想出了以下解决方案:

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') // PROBLEM

如您所见,它与函数作用域变量( var function )配合使用,但对块范围变量( let )无效).

As you can see it works fine with function-scoped variables (var and function), but fails on block scoped ones (let).

我如何编写一个上下文保留的 eval 包装器,该包装器还保留块作用域变量?

How can I write a context-preserving eval wrapper that would also preserve block-scoped variables?

代码在浏览器中运行,DOM和Workers完全可用.

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.

链接:

JavaScript:在一个虚拟机中进行所有评估 | https://vane.life/2016/04/03/eval-locally-with-persistent-context/

推荐答案

如果您运行 5,则本机JavaScript行为会返回 5 .让a = 2 本质上就是检查 twenty + 40 的值时程序最终语句中发生的事情.但是,一种快速的解决方法是同时收集结果,完整结果 fullResult 和该步骤的结果( stepResult ).有了这两种方法,一旦您的 evaluate()函数成功完成,我们就可以检查 stepResult 是否等于 undefined 分配新变量值时发生.

It is native JavaScript behavior to return 5 if you run 5; let a = 2 which is essentially what is happening here in the final statement of your program when you check for the value of twenty + 40. However, a quick workaround for this would be to gather both results, the full result fullResult and the result of just that step (stepResult). With both of these, once a success is met in your evaluate() function, we can check to see if stepResult is equal to undefined which occurs when assigning a new variable value.

在这种情况下,我们使用该值 undefined .否则,我们将使用 fullResult 的值,该值在您所提出的问题的每种情况下都适用:

If this is the case, we use that value undefined. Otherwise, we use the value of fullResult, which works in every case of the provided code in your question:

const pastEvals = [];

function* _EVAL(s) {
    while (1) {
        try {
            s = yield eval(s)
        } catch(err) {
            s = yield err
        }
    }
}

let _eval = _EVAL()
_eval.next()

function evaluate(expr) {
    pastEvals.push(expr)
    const fullResult = _eval.next(pastEvals.join(';')).value
    const stepResult = _eval.next(expr).value
    if (fullResult instanceof Error)
        console.log(expr, 'ERROR:', result.message)
    else
        console.log(expr, '===>', stepResult === undefined ? stepResult : fullResult);
}

evaluate('var ten = 10')
evaluate('function cube(x) { return x ** 3 }')
evaluate('ten + cube(3)')
evaluate('let twenty = 20')
evaluate('twenty + 40')

当尝试使用更高级的JS函数(例如 async / await fetch )时,您会遇到此问题,但应该这些简单的用例就可以正常工作.如果您需要构建适用于更高级用途的工具,则可能需要在幕后创建一个虚拟DOM,每次运行都创建并销毁一个新的虚拟DOM,还需要等到所有创建的承诺在迭代之间实现并完成后,因为这是与任何 fetch 相关的操作所必需的.

You will run into problems with this when trying to use more advanced JS functions such as async/await and fetch, but it should work just fine for these simpler use-cases. If you need to build something that works for more advanced uses, you may need to create a virtual DOM behind the scenes, create and destroy a new virtual DOM with each run, and also wait until any created promises are fulfilled and completed between iterations, as that is what would be required for any fetch-related operations.

这篇关于保持上下文的评估的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆