4

我有龙书,但它似乎没有处理那个话题......

在最现代的语言中,即使它们在代码中的出现是无序的,也可以使用某些变量。

例子

class Foo {
    void bar() {
        plonk = 42;
    }
    int plonk;
}

plonk在函数之后声明变量并不重要。

问题
是否有任何最佳实践/有用的模式来实现这一点?我想到了两种方法:

  1. 解析时为看不见的符号添加虚拟符号。解析声明时,这些虚拟对象将被它们的真实符号替换。解析后,我们可以检查是否有假人,如果有则输出错误。

  2. 解析时不要做任何符号的事情,而只创建 AST。在通过 AST 解析步骤并根据节点添加符号之后。例如,类节点添加子节点的符号并在之后处理它们。例如,语句块逐步遍历子级并在处理子级之前立即添加符号。

我希望方法 1. 对于“导入其他编译单元”之类的东西更容易,也更有用。

编辑:
我在方法 1 中看到的一个问题是需要对有序符号进行某种处理。例如,对于一个函数,在使用本地符号之前是不可能使用它的。

4

1 回答 1

2

如果可以,只需在解析期间构建 AST 和符号表。然后通过 AST 将符号与符号表条目相关联。这基本上是你的策略#2。

在一般情况下,策略 #1 的问题在于,在看到所有声明之前,您不一定知道同名的两个实例绑定到同一个符号。例如,考虑像 javascript 这样的语言,其中符号的绑定域是一个功能块(恕我直言,这是一个错误,但口味各不相同),但符号在使用前不需要声明。在这种情况下,我们将只考虑命名函数的符号。

伪代码(事实证明是合法的 javascript):

function outer() {
  return foo();

  function inner() {
    return foo();

    function foo() {
      return "inner's foo";
    }
  }

  function foo() {
     return "outer's foo";
  }
}

的两种用法foo指代不同的符号,直到达到foo.

策略 #2 的问题在于,在不了解所使用的符号的情况下,并不总是可以构建 AST。例如,在 C 中,您无法真正解析表达式,例如(x)(y)不知道x是类型名还是可以取消引用到函数中的东西。(也是一个错误,恕我直言,但我是谁?)。在 C++ 中,您还需要知道给定符号是否是模板。通常,这被描述为符号的“种类”,而不是“类型”。在 C++ 中,您不需要知道x要解析的“类型”是什么(x)(y);你只需要知道它是否有一个。出于这个原因,C++ 允许在声明之前使用某些符号,但如果声明是typedef.

撇开病态情况和宏处理器不谈,通常可以在解析期间定义范围,并将每个声明附加到一个范围。通常,作用域以一种相当简单的方式嵌套,所以一旦你构建了作用域树,你就可以查找给定当前作用域节点的任何符号,只需沿着树向上走直到找到符号。

在某些语言(如 python)中,声明是可选的和隐式的;在这种情况下,如果找不到符号,您可以在第二遍中将新定义附加到当前范围。

于 2013-07-20T22:35:47.583 回答