C# 编译器构建作用域问题

C# Compiler-Building Scoping Issue

我正在构建一个自定义的小型解释脚本语言,除了范围之外,一切都运行良好。 对于实际执行,我使用的是访问者模式:

我修改了模式以通过变量 Table:

public void visit(ProgrammTree proTree){
        VariableTable vt = new VariableTable();
        foreach (var t in proTree.getChildren()) {
            t.accept(this, vt);
        }   
    }

问题就在这里:

  public void visit(WhileTree whiletree, VariableTable vt) {
            var cond = (ConditionTree)whiletree.getChild(0);

            while (cond.accept(this, vt).toBoolean()) {
                 var clonedSubTable = new VariableTable(vt)
                foreach (Tree t in whiletree.getChildren()) {
                    t.accept(this, clonedSubTable );
                }

            }
        }

问题是循环内的更改不会在外部范围内执行。 你有实现这个的聪明方法吗?

你留下了一些模糊的东西,所以我将做出以下假设(请指出任何错误的地方):

  • 您的 VariableTable 将变量名直接映射到它们的关联值
  • 每当你分配一个值时,你直接将该值设置为 table 中的条目(不经过任何间接层)
  • 您克隆的变量 table 不保留对原始变量的引用,也不传播对原始变量的任何更改

因此,在这些假设下,问题是使用克隆 table 完成的任何赋值在原始 table 中将不可见,即使赋值变量已经存在于原来的table.

要解决此问题,有多种方法:

  1. 您可以使 table 将变量映射到内存位置而不是值。然后您将有另一个 table (或只是一个普通数组)将内存位置映射到值。这样,变量的 table 条目将永远不会改变:一旦变量被定义,它就会获得一个内存地址,并且该地址在变量消失之前不会改变。只有地址的值可能会改变。

  2. 一种让您不必管理自己的内存的方法的快速而肮脏的替代方法是在您的值周围添加一个 mutable 包装器,即 ValueWrapper class 与 setValue 方法。假设您克隆的 table 是原始文件的浅表副本,这意味着您可以在克隆的 table 的条目上调用 setValue,并且如果该条目已存在于原始文件中table,更改也会反映在原来的table。

  3. 最接近您当前代码的解决方案是将您的 table 变成 linked 结构。然后 new VariableTable(vt) 实际上不会复制任何东西,而只是创建一个新的空 table,父 link 指向原始 table。任何新条目都将插入到新 table 中,但对旧条目的访问将简单地传播到父 table.

即使您选择使用选项 1 或 2 来解决您当前的问题,出于性能原因,使用父 link 而不是不断复制 table 也是一个好主意。

仅使用解决方案 3 的缺点是,当您实施闭包时,您将 运行 再次陷入类似的问题。所以它实际上只能解决您当前的确切问题。

解决方案 1 的好处是它允许您完全控制您的内存,因此您可以自由地以任何您想要的方式实现与内存相关的功能。缺点也是一样。