`tagbody` 和 `go` 在 Common Lisp 中是如何实现的?
How are `tagbody` and `go` implemented under the hood in Common Lisp?
tagbody
和 go
在 Common Lisp 中是如何实现的?它是某种形式的 setjmp/longjmp 还是有更优雅的处理方式?
我正在编写一种用 C 语言实现的 lispy 语言,我想要这样的东西。
从实现的角度来看,如果您要解释 一个类似 Lisp 的程序,您可能会这样做:
- 输入
tagbody
后,开始 table 个目的地。 (符号→地址对的映射)
- 迭代
tagbody
中的每个表单
if (symbolp this-element)
,然后将地址(指向该表单的指针)存入table
- 否则,
(eval this-element)
照常
- 遇到
go
形式时,查找目标符号,并(破坏性地)将程序的 "current instruction" 指针更改为该值。然后,跳转到您的例程以获取下一条指令。
- 退出
tagbody
时,只需丢弃目的地 table。
目标 tables 将(最终)需要是一个堆栈(在旧的 Lisp 文档中称为 "push-down list" 或 PDL),因为您将通过动态范围向上搜索找到有问题的标签。请记住,在 Common Lisp 中,go
标签是独立于变量、函数、类 等的命名空间。
@jlahd 是正确的,它实际上与 C 中的(有限范围)goto
相同,但如果您正在解释代码,您实际上会覆盖 "program counter" 指针与储值。
将 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
到具有足够信息的最新上下文以保持 longjmp
根据正确的上下文,运行 unwind-protect
清理整个过程。这样,您可能仍想使用平台的异常处理功能,但仅用于设置展开框架 from/to 外部调用。
tagbody
和 go
在 Common Lisp 中是如何实现的?它是某种形式的 setjmp/longjmp 还是有更优雅的处理方式?
我正在编写一种用 C 语言实现的 lispy 语言,我想要这样的东西。
从实现的角度来看,如果您要解释 一个类似 Lisp 的程序,您可能会这样做:
- 输入
tagbody
后,开始 table 个目的地。 (符号→地址对的映射) - 迭代
tagbody
中的每个表单
if (symbolp this-element)
,然后将地址(指向该表单的指针)存入table- 否则,
(eval this-element)
照常 - 遇到
go
形式时,查找目标符号,并(破坏性地)将程序的 "current instruction" 指针更改为该值。然后,跳转到您的例程以获取下一条指令。 - 退出
tagbody
时,只需丢弃目的地 table。
目标 tables 将(最终)需要是一个堆栈(在旧的 Lisp 文档中称为 "push-down list" 或 PDL),因为您将通过动态范围向上搜索找到有问题的标签。请记住,在 Common Lisp 中,go
标签是独立于变量、函数、类 等的命名空间。
@jlahd 是正确的,它实际上与 C 中的(有限范围)goto
相同,但如果您正在解释代码,您实际上会覆盖 "program counter" 指针与储值。
将 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
到具有足够信息的最新上下文以保持 longjmp
根据正确的上下文,运行 unwind-protect
清理整个过程。这样,您可能仍想使用平台的异常处理功能,但仅用于设置展开框架 from/to 外部调用。