在 Racket 中,我可以像处理 s 表达式一样递归处理语法对象吗?

In Racket can I recursively process a syntax object like I would an s-expression?

假设我正在使用 s 表达式作为我正在编写的语言的 IR。然后我可能有一个 AST 看起来像

'(add (times (3 : int)
             (add (4 : int)
                  (5 : int)))
      (1 : int))

我可以毫不费力地递归处理。例如,要删除类型注释,我可以这样做

(define (erase-types term)
  (match term
    [`(,val : ,type)
     val]
    [`(,binop ,arg1 ,arg2)
     `(,binop ,(erase-types arg1) ,(erase-types arg2))]))

现在假设我想做同样的事情,但不是在编译时使用包含与顶部 AST 相同数据的语法对象。我尝试使用某种基于模式的宏,但似乎无法以递归方式使用 (define-syntax erase-types-or-whatever ...)

我还尝试将一个普通的 match 函数与 (require (for-syntax ...)) 放在一个单独的模块中,并在开始时做一个大的 syntax->datum,甚至做类似

的事情
(define (erase-types-or-whatever stx)
  (match (syntax-e stx)
    [...]))

但后来我到处都有 syntax->datum,我不得不用 datum->syntax 重新构造它,我真的不知道如何使用它(我只是把 #f 作为第一个论点?)这一切感觉都是绝对错误的做法。

这种语法树处理的正确方法是什么?当涉及到宏和语法时,这些文档也不是很有启发性。

#lang racket
(require syntax/parse)

(define-syntax : (λ (stx) (raise-syntax-error ': "used out of context" stx)))

(define (erase-types term)
  (syntax-parse term
    #:literals (:)
    [(val : type)      #'val]
    [(binop arg1 arg2) (with-syntax ([arg1 (erase-types #'arg1)]
                                     [arg2 (erase-types #'arg2)])
                         #'(binop arg1 arg2))]))

(erase-types #'(add (times (3 : int)
                           (add (4 : int)
                                (5 : int)))
                    (1 : int)))