Skip to content

define/with-datum -- defining pattern variables for datums #21

@shhyou

Description

@shhyou

Macro

The original macro: https://github.com/racket/racket/blob/8e83dc25f7f5767d9e975f20982fdbb82f62415a/racket/collects/racket/syntax.rkt#L22-#L59

A new implementation using syntax-parse:

#lang racket/base

(require syntax/parse/define
         syntax/datum
         (for-syntax racket/base
                     racket/syntax
                     racket/private/sc))

(provide define/with-datum)

(begin-for-syntax
  (define-syntax-class (fresh-temporary fresh-stx)
    #:attributes (fresh-var)
    (pattern name
             #:with fresh-var (format-id fresh-stx "~a" (generate-temporary #'name))))

  (define (count-ellipses-depth var...^n)
    (for/fold ([var...^n var...^n]
               [depth 0])
              ([current-depth (in-naturals 1)]
               #:break (not (pair? var...^n)))
      (values (car var...^n) current-depth))))

#|
Source:
  https://github.com/racket/racket/blob/8e83dc25f7f5767d9e975f20982fdbb82f62415a/racket/collects/racket/syntax.rkt#L22-#L59

  racket/collects/racket/syntax.rktracket/collects/racket/syntax.rkt
  Commit SHA: 8e83dc25f7f5767
  Line: 22-59
|#
(define-syntax-parse-rule (define/with-datum pattern rhs)
  #:attr matched-vars (get-match-vars #'define/with-datum
                                      this-syntax
                                      #'pattern
                                      '())
  #:with (((~var pvar (fresh-temporary #'here)) . depth) ...)
  (for/list ([x (attribute matched-vars)])
    (define-values (var depth)
      (count-ellipses-depth x))
    (cons var depth))

  (begin
    (define-values (pvar.fresh-var ...)
      (with-datum ([pattern rhs])
        (values (pvar-value pvar) ...)))
    (define-syntax pvar
      (make-s-exp-mapping 'depth (quote-syntax pvar.fresh-var)))
    ...))

;; auxiliary macro
(define-syntax-parse-rule (pvar-value pvar:id)
  #:attr mapping (syntax-local-value #'pvar)
  #:do [(unless (s-exp-pattern-variable? (attribute mapping))
          (raise-syntax-error #f "not a datum variable" #'pvar))]
  #:with value-var (s-exp-mapping-valvar (attribute mapping))
  value-var)

Example

(define/with-datum (x ((y z ...) ...))
  '("X" (("Y1" "Z11" "Z12")
         ("Y2" "Z21"))))

This code uses define/with-datum, the counterpart of define/with-syntax, to define pattern variables bound to plain data. Just like define/with-syntax, define/with-datum allows arbitrary patterns (with ellipses) that syntax-case supports.

In this example, the pattern variable x matches "X" in the list while the pattern ((y z ...) ...) defines the pattern variables y, z and matches (("Y1" "Z11" "Z12") ("Y2" "Z21")). The variable y at ellipsis depth 1 is bound to the value ("Y1" "Y2") and the variable z at ellipsis depth 2 is bound to the value (("Z11" "Z12") ("Z21")).

Here are a few examples using the defined pattern variables in datum templates:

(datum x)

(with-datum ([w "W"]) ;; currently supported
  (datum ((y w) ...)))

(datum
 ((^ z ... $) ...))

Before and After

The original macro: https://github.com/racket/racket/blob/8e83dc25f7f5767d9e975f20982fdbb82f62415a/racket/collects/racket/syntax.rkt#L22-#L59

All the procedural code in the original macro can be expressed in the pattern directives, #:attr, #:do and #:with. These pattern directives allow escaping into arbitrary expansion-time computation while retaining appropriate semantical meanings such as binding a pattern variable (#:with) or performing an imperative action (#:do).

Licence

I hereby license the code in this issue under the same MIT License that the Racket language uses:
https://github.com/racket/racket/blob/master/racket/src/LICENSE-MIT.txt

All the associated text in this issue is licensed under the Creative Commons Attribution 4.0 International License http://creativecommons.org/licenses/by/4.0/

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions