编译的最高阶段是什么,可以在符号table中输入"name"?

What is the highest stage in compilation, where a "name" can be entered into the symbol table?

以下为红龙书节选

  1. The symbol-table entry itself can be set up when the role of a name becomes clear, with the attribute values being filled in as the information becomes available.

  2. In some cases, the entry can be initiated from the lexical analyzer as soon as a name is seen in the input.

  3. More often, one name may denote several different objects, perhaps even in the same block or procedure. For example, the C declarations:

   int x; 
   struct x { float y, z; };          //(7.1)

use x as both an integer and as the tag of a structure with two fields. In such cases, the lexical analyzer can only return to the parser the name itself (or a pointer to the lexeme forming that name), rather than a pointer to the symbol-table entry.

The record in the symbol table is created when the syntactic role played by this name is discovered. For the declarations in (7.1), two symbol-table entries for x would be created; one with x as an integer and one as a structure.

在课文的介绍章节,我学到了一个技巧,就是把标识符输入到符号table中,就像词法分析器遇到的那样。(类似于点中所说的) 2 在摘录中)。

但是3中所说的,我并不知道。它说只有当声明的句法作用明确时才可以输入。在那种情况下,只有在 语法分析 完成后才必须输入 name

但是在执行 SDT 以使用如下语法向符号 table 添加类型时:

假定 lexeme 条目已存在于符号 table 中,我们为其添加类型。

我越来越糊涂了。如果符号 table 条目发生在语法分析之后,那么 SDT 如何与语法分析集成?

我将以 C 的特殊情况为例,因为很难用假设语言回答有关假设情况的问题。尽管如此,解析 C 所引发的具体问题可能表明您可能会使用哪种策略来处理其他具体问题。无论如何,像 Dragon book(或任何其他关于编译器构造的文本)这样的教科书的大部分内容都需要作为对可能性的探索而不是作为食谱来阅读。要是有几个菜谱可以抄袭,话题就没意思了。

不算宏(这是一个完全不同的讨论),C 有三个通用名称空间加上每个复合类型(即结构和联合)的名称空间。这些在 C 标准的 §6.2.3 中列出:

  1. 语句标签。

  2. 用于识别 structunionenum 类型的标签。

  3. 普通标识符,可以是变量名或类型别名

这些命名空间是完全独立的;使用名称作为标签或 union 类型的标签与其用作变量或类型别名没有任何关系,并且这些都与同一标识符的使用没有任何关系token 来命名一种或多种聚合类型的成员。虽然决定在哪个命名空间中查找特定标记非常简单,但这样做需要一些上下文(对于成员名称而言相当多)并且不太可能在词法扫描器中完成这项工作。因此,可以合理地假设词法扫描器会将标识符保留在某种名称目录中(以避免不必要地复制标识符字符串),并将其留给解析器使用它认为合适的标识符。

在大多数情况下,命名空间几乎可以立即解析,实际符号 table 条目将通过语法生成中的属性规则(或非严格语法驱动的编译器中的道德等价物。

例如,在类型声明中,聚合标记始终跟在标记 enumstructunion 之后,而标签始终跟在 : 之后和一份声明。相关的产品是明确的和相对低级的,并且可以很容易地将新创建的标识符输入正确的符号 table,然后再在同一名称空间中再次使用相同的标识符。

这是 C 语法的一部分:

type-specifier
    : ... (lots of keywords like `int`, `double`, etc.)
    | struct-or-union-specifier
    | enum-specifier
    | ...
struct-or-union-specifier
    : struct-or-union identifier
    | struct-or-union identifier '{' struct-declaration-list '}'
    | struct-or-union '{' struct-declaration-list '}'
struct-or-union
    : "struct"
    | "union"
...

为了正确处理struct-or-union-specifier的前两个产生式中的identifier,我们需要在符号table中查找聚合标签的标识符并设置一些属性到该符号 table 条目。我们还需要处理未标记的 structunion 类型,它们还需要一个符号 table 条目来保存有关聚合类型的数据。 (调用这些符号 table 条目有点误导,因为未标记的类型实际上从未输入任何符号 table 但关于类型的其他信息是相同的,无论类型是否被标记,所以它使得将其保存在 SymbolTableEntry 数据对象中是有意义的。)

聚合标签一经输入即为合法;特别是,作为 struct-declaration-list 中成员声明的一部分。因此,我们将需要在 struct-declaration-list 生产过程中查找或创建条目,就在 { 标记之前。但是这种形式继承的属性很多,操作起来也没什么难度。我们只是添加一个继承的属性规则:

struct-or-union-specifier
    : struct-or-union identifier 
                     { struct-or-union.entry := new_tag_symbol(identifier.string); }
      '{' struct-declaration-list '}'
                     { /* Attach the definitions in struct-declaration-list
                          to the aggregate type entry in struct-or-union.entry
                        */
                     }

(untagged的规则是创建一个新的未命名聚合类型,而不是试图在符号中输入名称table,但原理没有什么不同。)

这与在标签符号 table 中输入标签的方式非常相似(与任何其他符号 table 不同,它始终限定在当前函数范围内)。我不会不过,用范围使大纲复杂化。):

labeled-statement:  identifier
                           { labeled-statement.label := define_label_name(identifier.string); }
                    ':' statement

看来我们需要在规则中间创建属性,我在上面的例子中就是这样做的,因为声明使用自己的标签是合法的(尽管显然是不好的风格):

    again: if (try() != SUCCEED) { goto again; }

然而,这并不是真正需要的,因为 C 不要求在使用前声明标签。 (事实上​​,它没有提供声明标签而不将其附加到语句的机制。)因此解析器需要能够在使用标签的任何地方处理尚未定义的标签。最可能的解决方案是在 jump-statement.

的属性规则中查找或将标签输入标签符号 table

None 好像特别复杂。我希望你同意。

向新创建的聚合类型添加成员可能比较复杂。成员的声明看起来与局部或全局变量的声明没有任何不同;唯一的区别是成员声明在 struct-declaration-list 中解析。如上所示,聚合类型的符号 table 已经创建(在 new_tag_symbol 中),并且可以通过从 struct-declaration-list 继承来访问。这个继承属性链最终如何到达声明将因编译器的不同而有很大差异,但实现继承属性链的标准技术将是适用的。