Racket Macro to auto-define functions given a list

自作多情 提交于 2019-12-01 11:01:39

I see a couple of errors in your code:

  1. Your *oxyanion-tests* is a runtime value, but you need its values to use as function name identifiers, so it must be available at compile time.
  2. The syntax around the result of syntax-rules is implicit. So with syntax-rules, you only get the macro template language (see the docs for syntax for more info). Thus you can't do the datum->syntax that you are trying to do. You have to use syntax-case instead, which allows you to use all of Racket to compute the syntax objects you want.

Here's what I came up with:

#lang racket
(require (for-syntax racket/syntax)) ; for format-id

(define-for-syntax *oxyanion-tests*
  ;           name         cation
  (list (list "aluminate"  "Al")
        (list "borate"     "B")
        (list "gallate"    "Ga")
        (list "germanate"  "Ge")
        (list "phosphate"  "P")
        (list "sulfate"    "S")
        (list "silicate"   "Si")
        (list "titanate"   "Ti")
        (list "vanadate"   "V")
        (list "stannate"   "Sn")
        (list "carbonate"  "C")
        (list "molybdate"  "Mo")
        (list "tungstate"  "W")))

(define ((*ate? elem) s-formula)
  (or (regexp-match? 
       (regexp (string-append "\\(" elem "[0-9.]* O[0-9.]*\\)")) 
       s-formula)
      (regexp-match?
       (regexp (string-append "(^| )" elem "[0-9.]* O[2-9][0-9.]*")) 
       s-formula)))

(define-syntax (define-all/ate? stx)
  (syntax-case stx ()
    [(_)
     (let ([elem->fn-id 
            (λ (elem-str)
              (format-id 
               stx "~a?" 
               (datum->syntax stx (string->symbol elem-str))))])
       (with-syntax 
         ([((ate? cation) ...)
           (map 
            (λ (elem+cation)
              (define elem (car elem+cation))
              (define cation (cadr elem+cation))
              (list (elem->fn-id elem) cation))
            *oxyanion-tests*)])
         #`(begin
             (define (ate? sform) ((*ate? cation) sform))
             ...)))]))

(define-all/ate?)
(module+ test
  (require rackunit)
  (check-true (borate? "B O3"))
  (check-true (carbonate? "C O3"))
  (check-true (silicate? "Si O4")))

The key is the elem->fn-id function, which turns a string into a function identifier. It uses datum->syntax with stx as the context, meaning the defined function will be available in the context where the macro is invoked.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!