用解释语言存储变量的数据结构



我正在设计自己的实验性脚本语言,目的是将其嵌入到我更大的应用程序中。

我想做的几乎所有事情都编程得很顺利,但在内存中存储变量的"简单"行为似乎是最困难的部分。我不知道如何存储它们以允许所有类型检查、全局变量和特殊标志。首先看一个示例代码:

a = 1
b = 2
someFunction()
  print(a)   --> This should read the global variable and print `1`
  a = 3      --> Now `a` should become a local variable of this function
                 and the global `a` remain unchanged
  x = 4      --> `x` should always be local of this function
end

我将变量的"局部性"称为它们的level,因此嵌套块中的变量具有更高的级别。在上面的代码中,ab是级别1的变量。someFunction的局部变量将具有级别2。函数的第一行应该读取全局变量a(级别1),但第二行应该再次创建一个名为a的变量,但级别2会从该点开始遮蔽全局a。第三行应该创建级别为2的变量x。如何在内存中存储和跟踪所有这些?

到目前为止我尝试了什么:

方法1:将variable=>value的地图存储在级别阵列中:

variables
{
    level=1 //global variables
    {
        a => 1,
        b => 2
    },
    level=2 //function variables
    {
        a => 3,
        x => 4
    }
}

但这将使变量查找非常缓慢,因为必须搜索给定变量的所有级别。

方法2:将(变量、级别)对存储为映射的密钥:

variables
{
    (a, 1) => 1, //global
    (b, 1) => 2, //global
    (a, 2) => 3, //function
    (x, 2) => 3  //function
}

这和以前有同样的问题,因为我们必须尝试对(变量,级别)与给定变量的所有可能级别。

我应该使用什么方法来获得最佳内存使用率和最快的访问时间?

附加说明:

我知道在其他"真实"语言中如何在堆栈和堆上管理变量,但我发现在解释语言中这样做很难。"Lua和Python一定不是这样做的,"我一直认为。如果我错了,请纠正我。我正在尝试将变量存储在映射和内部C++结构中。

最后,这就是我如何表示一个变量。你认为它很大,可以有更高效的内存表示吗?(我也试着把"Level"作为成员放在这里,但它也遇到了和另一个相同的问题。)

struct Member
{
    uchar type;  //0=num, 1=str, 2=function, 3=array, etc
    uchar flags; //0x80 = read-only, 0x40 = write-only, etc
    union {
        long double value_num;
        char* value_str;
        int value_func;
        //etc
    };
};

与数组类似,一件简单的事情就是维护一堆映射。每个映射都包含该作用域的绑定。若要绑定变量,请将其添加到顶部映射;要查找变量,请从堆栈的顶部开始,当到达包含该变量绑定的映射时停止。搜索需要一点时间,但从顶部/末端开始,你只需要搜索直到找到它——在大多数情况下,这个搜索不会很长。

您还可以通过将此逻辑封装在Environment类中来使堆栈隐式,该类具有本地绑定和用于解析未知变量的继承环境。需要进入一个新的范围吗?以当前环境为基础创建一个新环境,使用它,然后在作用域结束时丢弃它。根/全局环境只能有一个null继承的环境。这是我可能会做的。

值得注意的是,如果在函数内部,您无法访问调用方函数中的任何变量,则会降低您需要查看的级别数。例如:

variable a;
function one() {
    variable b;
    // in this function, we can see the global a, local b
    two();
}
function two() {
    // in this function, we can see the global a, local c
    // we cannot see the local b of our caller
    variable c;
    while (true) {
        variable d;
        // here we can see local d, local c, global a
    }
}

其思想是,函数边界限制了变量的可见性,全局范围是"特殊的"。

话虽如此,您可以考虑删除全局变量的特殊性,但允许代码指定他们想要访问非局部变量

variable a;
function one() {
    global a; // or upvar #0 a;
    variable b;
    // in this function, we can see the global a, local b
    two();
}
function two() {
    // in this function, we can see the local c
    // and the local b of our caller
    // (since we specifically say we want access to "b" one level up)
    upvar 1 b;
    variable c;
}

它一开始看起来很复杂,但一旦你习惯了它,就很容易理解了(upvar是Tcl编程语言的一个构造)。它允许您访问调用方作用域中的变量,但它要求您指定变量的确切来源,从而避免了一些昂贵的查找(1是调用堆栈的一级,2是调用堆栈上的两级,#0是"特殊的",表示"最上面的调用堆栈,全局)

相关内容

  • 没有找到相关文章

最新更新