c – 编译器构造:处理对无序符号的引用

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

在大多数现代语言中,即使它们在代码中的外观是无序的,也可以使用某些变量.

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

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


有没有最佳实践/有用模式如何实现?我想到了两种方法:

>解析时为看不见的符号添加虚拟符号.解析声明时,这些假人会被真实符号取代.在解析之后,我们可以检查是否还有假人,如果是,则输出错误.
>解析时不要做任何符号,但只创建AST.解析完步骤后,根据节点添加符号.对于例如类节点添加子节点的符号并在之后处理它们.对于例如语句块逐步执行子项并在处理子项之前立即添加符号.

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

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

最佳答案 如果可以,只需在解析期间构建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)中,声明是可选的和隐式的;在这种情况下,如果找不到符号,则可以在第二遍中将新定义附加到当前作用域.

点赞