在Racket中匹配AST

时间:2017-05-11 04:29:33

标签: racket

我想编写一个与输入参数的不同语法匹配的函数,例如

(define (foo e) 
  (match e
    [int (print "int")]
    [(and _ _) (printf "and")])
)

这样我可以用:

来调用它
(foo 1) --> prints "int"
(foo (and #t #f)) --> prints "and"

但这似乎不起作用。我做错了什么?

2 个答案:

答案 0 :(得分:2)

对此没有简短而完整的回应。

首先,你不能用程序做到这一点。您可以使用谓词来确定某些内容是否为某个数字,例如number?,但您无法使用此and来捕获foo之类的语法。

但真正非常非常重要的一点是你无法捕捉到这样的事情:当e看到(and #t (+ 3 4))时,扩展器已经扩展了任何语法并评估了表达式。也就是说,例如,(if #t (+ 3 4) #f)已转换为类似e的表达式,并且在您在过程中看到e时已经计算了此表达式,在这种情况下7只是quote。在Racket中,在调用过程之前评估过程的参数。这意味着,大致应用了所有语法变换器,并评估了生成的扩展表达式,然后在结果值上调用过程。

如果您只关心修改s表达式,可以(quote (and 2 3)参数并使用结果列表。 'and将为您提供包含符号define-syntax的列表,而不是语法,因此这可能不是您想要的。我提到它有些完整。

在Racket中管理问题的方法是使用宏。同样,问题是在调用过程时,作为参数传递给过程的表达式已经已经被评估,因此您必须在#lang racket/base (require (for-syntax racket/base syntax/parse)) (define-syntax (foo stx) (syntax-parse stx #:literals (and) [(_ i:number) (syntax (displayln "int"))] [(_ (and a b)) (syntax (displayln "and"))])) 级别工作才能捕获表达式在他们扩展和评估之前。也就是说,要解决您的特定问题,您需要类似于将某些语法作为参数并返回语法的过程:

stx

此过程采用一些语法并产生一些其他语法。语法参数(and #t #f)(可以命名为任何东西,它只是约定)必须匹配一个子句,否则它是一个语法错误。当您定义语法时,您实际上是在创建一种新的编程语言,这是该语言的新构造。如果匹配,例如案例#'(foo (and #t #f)),则会将#'(displayln "and")替换为{{1}}。在评估之前,它实际上不会显示“和”。

要开始使用宏,请参阅the Racket Guidethe Racket Reference

答案 1 :(得分:0)

问题是匹配模式int匹配所有内容。

> (match 42
      [int  "foo"]
      [_    "bar"])
"foo"

如果您想匹配符号int,则需要撰写'int

> (match 'int
      ['int  "foo"]
      [_     "bar"])
"foo"
> (match 42
      ['int  "foo"]
      [_     "bar"])
"bar"

另请注意,在您的示例中:(foo (and #t #f))在调用函数之前计算函数调用的参数。也就是说,首先使用(and #t #f)评估#f作为结果。然后foo被调用:(foo #f)

如果您想使用列表和符号表示法调用foo,请使用:(foo '(and #t #f))