Lua语言上下文/范围实现

时间:2014-01-19 15:54:11

标签: c# .net lua clr

我正在用C#实现一个Lua解释器,我偶然发现了一个性能问题,我认为这是由不正确的设计引起的: 在我的实现中,作用域以分层方式排列,也就是说,每个作用域都有一个父作用域,它可以是null,也可以不是null;每次设置或请求变量时,范围都会检查指定的变量是否包含在其中。如果没有,请求其父级执行相同操作,或者如果没有父级,则创建它/返回nil。 问题是范围使用下面的Dictionary<string, LuaObject>,并且Get / Set函数是递归的。有没有更好的方法呢?我一直在考虑不同的方法,我发现了这个方法,它不是很优雅也不是很有效(受到V8和C#封装的启发):

将类型和字段用作变量。

  • 每当您请求之前未声明的变量时,请使用继承前一个变量的Reflection创建一个新类型。
  • 如果声明了函数,请检查它是否使用在外部作用域中声明的变量。如果是这样,请重新创建该类型的字段,并使外部变量指向新变量。 (我知道这一点解释得非常糟糕,但它只不过是C#闭包实现的标准方式。如果你不明白,你可以去阅读)

是否有更简单/更优雅的解决方案? Lua如何在内部实施?

1 个答案:

答案 0 :(得分:3)

它的完成方式in the reference implementation,以及类似语言的许多其他实现:在编译时,

  • 在编译时弄清楚哪个名称指的是哪个范围。
  • 对于每个范围(全局除外),枚举变量并为每个范围分配索引。
  • 不使用字节码中的变量名称,而是使用上一步中的索引。

然后在执行期间,

  • 将本地的值存储在一个数组中(每个激活记录)。读写操作码带有必要的索引。
  • 像Lua表一样实现全局变量(免费获取nil以获取缺失的变量。)

这比使用动态创建的类型更简单,更容易,更便携。它甚至可能更快,但肯定不会慢得多。

关闭支持主要是正交的。所谓的upvalues在本地数组仍处于活动状态时引用(一个插槽)本地数组,并在本地数组死亡时采用关闭值。您可以使用原始设计执行类似操作。为了正确起见,您必须注意只为每个已关闭的变量创建一个upvalue,并且展平闭包。例如,以下代码中的g也必须关闭x,以便在创建h闭包时它仍然存在:

function f()
  local x = 1
  function g()
    function h()
      return x
    end
    return h
  end
  return g
end

print(f()()())