Compiler construction 编译器如何处理符号表中的默认值

Compiler construction 编译器如何处理符号表中的默认值,compiler-construction,programming-languages,default-value,symbol-table,Compiler Construction,Programming Languages,Default Value,Symbol Table,某些编程语言允许参数在函数调用中具有默认值,或者结构中的属性具有默认值。与python或javascript一样,当我们在函数调用中省略一些参数时,函数中的默认值将被替换(与struct在属性中也可以有default相同)。那个么编译器实际上是如何处理这个问题的呢?特别是在符号表中?我想为指向默认值的指针添加一个额外的属性,我猜默认值在运行时被推到堆栈上?请让我知道我是否正确。谢谢 更新: 这里的默认值是指在Python中,下面的程序参数b具有默认值0 def foo(a, b=0):

某些编程语言允许参数在函数调用中具有默认值,或者结构中的属性具有默认值。与python或javascript一样,当我们在函数调用中省略一些参数时,函数中的默认值将被替换(与struct在属性中也可以有default相同)。那个么编译器实际上是如何处理这个问题的呢?特别是在符号表中?我想为指向默认值的指针添加一个额外的属性,我猜默认值在运行时被推到堆栈上?请让我知道我是否正确。谢谢

更新: 这里的默认值是指在Python中,下面的程序参数
b
具有默认值
0

def foo(a, b=0):
    return a+b
print foo(1)
print foo(1, 1)

我们将得到结果
0
1
。那么编译器在运行时或编译时是如何处理这个问题的呢?

无论您如何处理,都必须将默认值与特定标识符相关联

参数列表和结构成员形成小型名称空间,编译器通常通过为这些名称空间构建符号表和跟踪名称空间之间关系的大型符号表来跟踪它们

由于编译器通常在这些小名称空间中已经有符号表条目,这些条目将其他信息与符号(例如,任何类型信息)关联起来,因此这是记录符号的一个非常自然的地方


有一个问题是,初始值到底要记录什么。一件简单的事情就是记录一个指向AST的指针,该指针表示初始/默认值。除此之外,还必须记录应该在其中计算该表达式的上下文(例如,“环境”)。此上下文通常是隐式的,因为它通常与定义结构/参数的上下文相同,并且该信息存储在将所有内容粘合在一起的较大符号表中。

无论如何操作,都必须将默认值与特定标识符相关联

参数列表和结构成员形成小型名称空间,编译器通常通过为这些名称空间构建符号表和跟踪名称空间之间关系的大型符号表来跟踪它们

由于编译器通常在这些小名称空间中已经有符号表条目,这些条目将其他信息与符号(例如,任何类型信息)关联起来,因此这是记录符号的一个非常自然的地方


有一个问题是,初始值到底要记录什么。一件简单的事情就是记录一个指向AST的指针,该指针表示初始/默认值。除此之外,还必须记录应该在其中计算该表达式的上下文(例如,“环境”)。此上下文通常是隐式的,因为它通常与定义结构/参数的上下文相同,并且该信息存储在较大的符号表中,将所有内容粘在一起。

当计算函数调用的参数列表时,代码生成器可以通过将列表与存储在符号表中的函数签名进行比较来判断缺少哪些参数。一旦识别出需要替换的参数,只需将默认值放入参数寄存器(或堆栈帧)即可。如果您正在处理一种OO语言,并且形式参数是一个对象,那么您可能会将0(NULL)推入堆栈的下一个字中。如果参数是内置原语,则将推送语言指定的任何默认值

当然,编译器可能会有所不同,但在过于简单的编译器中,并不一定要在符号表中关联默认值。首先,让我们明确一点,符号表记录所有变量声明。您必须存储有关这些声明的信息(例如,类、方法、类型、行、字符等),但当只有少数类型,因此只有少数可能的默认值时,肯定不需要为每个变量记录默认值

某些语言(Java/C++)指定具有未初始化对象属性的类的默认值为NULL。在实现类似的功能时,当代码生成器创建类构造函数时,它一定会生成将0放置在映射到该属性的对象内存中的代码(假设所有内容都是基于指针的)。当您要生成构造函数,并且在类属性列表(AST节点)上迭代时,如果该属性没有初始化表达式,请调用一个方法来执行默认操作

private void genConstructor(int classId) {

     //allocate new object

     codeGen.write("li $a0, "+classId); 
     codeGen.write("jal Runtime.newObject");

     //call parent constructor

     codeGen.write("move $a0, $v0"); 
     codeGen.write("jal ParentInit");

     //initialize the attributes this class has declared

     for(Attributes a: getAttributes(classId)) {

        //provide default value
        if(a.getInitExpr() == null)
            doDefault(a.getType(), a.getNum());
        else
            doInit(a.getInitExpr(), a.getNum());
     }
}

// An 'Int' default value is 1, everything else is 0 (Objects included)
// $t0 has the object address
// attributes start at the 5th word of each object 
private void doDefault(String type, int attrNum) {
     switch(type) {
          case "Int": {
                codeGen.write("sw $one, "+(5+attrNum)+"($t0)");
          }
          default: {
                codeGen.write("sw $zero, "+(5+attrNum)+"($t0)");
          }
     }
}
更新:

我想知道编译器如何处理程序员设置的默认值 而不是“类”或“类型”的“默认”


我假设你在C++构造函数中讲的是类似默认的ARG的东西。在这种情况下,正如Ira提到的,在符号表记录中创建一个字段听起来是最简单的方法。为缺少的参数生成代码时,请在表中查找默认值。

计算函数调用的参数列表时,代码生成器可以通过将列表与存储在符号表中的函数签名进行比较来判断缺少哪些参数。一旦识别出需要替换的参数,只需将默认值放入参数寄存器(或堆栈帧)即可。如果您正在处理一种OO语言,并且形式参数是一个对象,那么您可能会将0(NULL)推入堆栈的下一个字中。如果参数是内置原语,则将推送语言指定的任何默认值

当然,编译器可能会有所不同,但在过于简单的编译器中,并不一定要在符号表中关联默认值。首先,让我们明确一点,符号表记录所有变量声明。您必须存储有关这些声明的信息(例如,类、方法、类型、行、字符等),但肯定不需要为这些声明记录默认值