如何在Common Lisp中实现`tagbody`和`go`?

时间:2015-05-22 03:32:43

标签: compiler-construction common-lisp

Common Lisp中如何实现tagbodygo?它是某种形式的setjmp / longjmp还是有一种更优雅的方式来处理它?<​​/ p>

我正在写一个用C实现的lispy语言,并希望有类似的东西。

2 个答案:

答案 0 :(得分:3)

从实现的角度来看,如果你解释类似Lisp的程序,你可能会做一些类似的事情:

  • 输入tagbody后,开始一个目的地表。 (符号地图→地址对)
  • tagbody
  • 中迭代每个表单
  • if (symbolp this-element),然后将地址(指向该表单的指针)存储到表
  • 否则,(eval this-element)像往常一样
  • 遇到go表单时,查找目标符号,并(破坏性地)将程序的“当前指令”指针更改为该值。然后,跳转到您的例程以获取下一条指令。
  • 退出tagbody时,只需丢弃目标表。

目标表(最终)需要是一个堆栈(在旧的Lisp文档中称为“下推列表”或PDL),因为您将向上搜索动态范围以查找相关标签。请记住,在Common Lisp中,go标记是变量,函数,类等的独立命名空间。

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

答案 1 :(得分:3)

将Common 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 ++异常)集成,并且平台可能已经有一堆处理程序,那么它可能会得到更好的回报,从而自动运行unwind-protect清理表单直到某个堆栈框架

如果您希望以最少的工作量保持便携性,则可以管理setjmp上下文的线程局部堆栈,其中longjmp到最近的上下文,并保留足够的信息{{1}正确运行longjmp清理表格。这样,您可能仍然希望使用平台的异常处理功能,但只能设置从/向外部调用展开帧。