【问题标题】:How is Lexical Scoping implemented? [closed]词法作用域是如何实现的? [关闭]
【发布时间】:2011-01-23 23:00:09
【问题描述】:

几年前,我开始为包含程序员定义的函数的小型领域特定语言编写解释器。

起初我使用一个简单的符号表堆栈来实现变量范围。但是现在我想转向正确的词法范围(可以选择闭包)。谁能解释词法范围背后的数据结构和算法?

【问题讨论】:

标签: programming-languages closures compiler-construction lexical-scope


【解决方案1】:

没有一种正确的方法可以做到这一点。重要的是清楚地说明您希望提供的语义,然后数据结构和算法将随之而来。

【讨论】:

  • 当然。我总是可以尝试自己推导出整个事情。 :-) 但是对于许多很好理解的编程任务,通常有现有的解决方案,这些解决方案已经为人所知并被广泛教授和采用,不是吗?
  • 对您的问题的评论中引用的那本书,或者封面上有龙的著名书籍,会解决这个问题。
【解决方案2】:

要在解释器中获得正确的词法作用域和闭包,您需要做的就是遵循以下规则:

  • 在您的解释器中,变量总是在环境表中查找由调用者传入或保存为变量,而不是某个全局环境堆栈。您的 eval 操作的签名类似于 eval(expression, env) => value
  • 当解释代码调用函数时,环境传递给该函数。你的函数应用操作的签名就像apply(function, arguments) => value
  • 当一个解释函数被调用时,它的主体被评估的环境是创建函数定义的环境,并且与调用者没有任何关系。所以如果你有一个局部函数,那么它就是一个闭包,也就是一个包含字段{function definition, env-at-definition-time}的数据结构。

用 Python-ish 语法扩展最后一点:

x = 1
return lambda y: x + y

应该像它一样被执行

x = 1
return make_closure(<AST for "lambda y: x + y">, {"x": x})

其中第二个 dict 参数可能只是当前环境,而不是当时构建的数据结构。 (另一方面,保留整个环境而不仅仅是封闭的变量可能意味着程序会出现令人惊讶的内存泄漏,因为闭包会保留不需要的东西。这值得在任何“实用”语言实现中修复,但不当您只是在尝试语言语义时。)

【讨论】:

    【解决方案3】:

    【讨论】:

      【解决方案4】:

      有许多不同的方法来实现词法作用域。以下是我最喜欢的一些:

      • 如果您不需要超快的性能,请使用纯函数式数据结构来实现符号表,并通过包含指向代码的指针和指向符号表的指针的对来表示嵌套函数.

      • 如果您需要本机代码速度,我最喜欢的技术在 Simon Marlow 和 Simon Peyton Jones 的 Making a Fast Curry 中进行了描述。

      • 如果您需要本机代码速度,但柯里化函数并不那么重要,请考虑 closure-passing style

      【讨论】:

      • 关于柯里化的论文有时显得过于非正式。例如,参见第 2 节最后一段的第 2 句:“调用是对一个已知函数——编译器可以“看到”其定义的函数。通过“see”,它们意味着该函数出现在使用它的词法范围内。否则,如果您像我一样试图弄清楚如何制作解释器,我想这是一本好书。
      【解决方案5】:

      Stroustrup 在第一个 C++ 编译器中实现了这一点,每个作用域只有一个符号表,以及一个遵循作用域的链接规则,直到找到定义。这完全取决于您的精确语义。确保先确定这些。

      计算机编程艺术第 1 卷中的 Knuth 给出了一种 Cobol 符号表算法,通过链接确定范围。

      【讨论】:

        猜你喜欢
        • 2015-01-09
        • 2017-05-21
        • 1970-01-01
        • 1970-01-01
        • 2017-06-22
        • 2010-09-19
        • 2013-06-26
        相关资源
        最近更新 更多