Compiler construction 在Common Lisp中,“tagbody”和“go”是如何在hood下实现的?

Compiler construction 在Common Lisp中,“tagbody”和“go”是如何在hood下实现的?,compiler-construction,common-lisp,Compiler Construction,Common Lisp,如何在Common Lisp中实现tagbody和go?它是某种形式的setjmp/longjmp,还是有一种更优雅的处理方式 我正在编写一种用C语言实现的lispy语言,希望有类似这样的语言。从实现的角度来看,如果您正在解释一个类似Lisp的程序,您可能会执行以下操作: 输入tagbody后,开始一个目的地表。(符号地图)→地址对) 迭代标记体中的每个表单 如果(symbolp此元素),则将地址(指向该表单的指针)存储到表中 否则,(评估此元素)与往常一样 遇到go表单时,查找目标符号,并(

如何在Common Lisp中实现
tagbody
go
?它是某种形式的setjmp/longjmp,还是有一种更优雅的处理方式


我正在编写一种用C语言实现的lispy语言,希望有类似这样的语言。

从实现的角度来看,如果您正在解释一个类似Lisp的程序,您可能会执行以下操作:

  • 输入
    tagbody
    后,开始一个目的地表。(符号地图)→地址对)
  • 迭代
    标记体中的每个表单
  • 如果(symbolp此元素)
    ,则将地址(指向该表单的指针)存储到表中
  • 否则,
    (评估此元素)
    与往常一样
  • 遇到
    go
    表单时,查找目标符号,并(破坏性地)将程序的“当前指令”指针更改为该值。然后,跳转到例程获取下一条指令
  • 退出标记体时,只需放弃目标表即可
目标表(最终)需要是一个堆栈(在旧的Lisp文档中称为“下推列表”或PDL),因为您将在动态范围中向上搜索以找到有问题的标记。请记住,在公共Lisp中,
go
标记是与变量、函数、类等分离的名称空间


@jlahd是正确的,它实际上与C中的(有限范围)
goto
完全相同,但是如果您解释代码,实际上将用存储的值覆盖“程序计数器”指针

将通用Lisp的
go
简化为其他语言
goto
是太简单了

在Common Lisp中,
go
可以展开堆栈。例如:

(tagbody
    (mapc #'(lambda (el1 el2)
              (format t "el1: ~a, el2: ~a~%" el1 el2)
              (when (or (null el1) (null el2))
                (go stop)))
          list1
          list2)
  stop)
如果您是用C语言实现Common Lisp,那么非展开的
go
可能是常规的
goto
,但展开的
go
需要
setjmp
/
longjmp
或等效功能,具有适当的堆栈展开,如果需要,还需要常规的
goto
,即,如果标记的Lisp表单不是
setjmp
之后的C语句或表达式

如果您有足够的时间对其进行抽象,您可能会希望使用操作系统的异常处理。如果您以后想与其他语言的特征(如C++异常)集成,则可能会更好地得到回报,并且平台可能已经有一个处理程序栈,因此运行<代码> unWravy保护< /COD>清理窗体自动达到某个堆栈帧。 如果您想让它以最小的工作量保持可移植性,您可以管理一个线程本地堆栈
setjmp
上下文,其中
longjmp
到具有足够信息的最新上下文,以保持
longjmp
一直运行
unwind protect
清理表单。通过这种方式,您可能仍然希望使用平台的异常处理功能,但只需要设置从/到外部调用的展开帧