我正在设计自己的实验性脚本语言,以便将其嵌入到我更大的应用程序中。
我想做的几乎所有事情都是顺利编程的,但在内存中存储变量的“简单”行为似乎是最困难的部分。我不知道如何存储它们以允许所有类型检查,全局变量和特殊标志。首先看一下示例代码:
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
我将变量的“locality”称为level
s,因此嵌套块中的变量具有更高的级别。在上面的代码中,a
和b
是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
};
};
答案 0 :(得分:5)
与数组类似,一件容易的事就是维护一堆地图。每个映射都包含该范围的绑定。要绑定变量,请将其添加到顶部地图;查找变量,从堆栈顶部开始,到达包含该变量绑定的映射时停止。搜索需要一点点,但从顶部/结尾开始,您只需搜索直到找到它 - 在大多数情况下,此搜索不会很长。
您还可以通过将此逻辑封装在具有本地绑定的Environment
类和用于解析未知变量的继承环境中来隐式堆栈。需要进入一个新的范围?创建一个以当前环境为基础的新环境,使用它,然后在范围完成时将其丢弃。根/全局环境可以只具有null继承环境。这就是我可能会做的。
答案 1 :(得分:2)
值得注意的是,如果在函数内部,您无法访问调用函数中的任何变量,则会降低您需要查看的级别数。例如:
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在说“最上面的调用堆栈,全局”
时是“特殊的”