This commit is contained in:
Jack Firth 2015-03-09 01:40:34 -07:00
parent c3ddab4f59
commit 3d9cef39a0
19 changed files with 304 additions and 30 deletions

6
.gitignore vendored
View File

@ -1,6 +1,8 @@
compiled/* **/compiled
*.bak *.bak
*.html *.html
*.css *.css
*.js *.js
*.2 *.2
*~

View File

@ -13,7 +13,6 @@
(with-derived-ids #'name ([?-expander-type "~a-expander-type"] (with-derived-ids #'name ([?-expander-type "~a-expander-type"]
[make-?-expander "make-~a-expander"] [make-?-expander "make-~a-expander"]
[?-expander? "~a-expander?"] [?-expander? "~a-expander?"]
[?-expander-stx? "~a-expander-stx?"]
[define-?-expander "define-~a-expander"] [define-?-expander "define-~a-expander"]
[expand-all-?-expanders "expand-all-~a-expanders"]) [expand-all-?-expanders "expand-all-~a-expanders"])
#'(begin #'(begin
@ -22,8 +21,6 @@
(expander ?-expander-type transformer)) (expander ?-expander-type transformer))
(define-for-syntax (?-expander? v) (define-for-syntax (?-expander? v)
(expander-of-type? ?-expander-type v)) (expander-of-type? ?-expander-type v))
(define-for-syntax (?-expander-stx? v)
(expander-stx-of-type? ?-expander-type v))
(define-syntax-rule (define-?-expander expander-name transformer) (define-syntax-rule (define-?-expander expander-name transformer)
(define-syntax expander-name (make-?-expander transformer))) (define-syntax expander-name (make-?-expander transformer)))
(define-for-syntax (expand-all-?-expanders stx) (define-for-syntax (expand-all-?-expanders stx)

View File

@ -0,0 +1,16 @@
#lang racket
(require (for-syntax "scoped-transformers.rkt"))
(provide define-syntax-with-scoped-pre-transformers)
(define-syntax define-syntax-with-scoped-pre-transformers
(syntax-rules ()
[(_ name ([stx-lens pre-transformer] ...) transformer-expr)
(define-syntax name
(with-scoped-pre-transformers transformer-expr
(list (list stx-lens pre-transformer) ...)))]
[(_ (name stx) ([stx-lens pre-transformer] ...) transformer-body ...)
(define-syntax-with-scoped-pre-transformers name
([stx-lens pre-transformer] ...)
(lambda (stx) transformer-body ...))]))

View File

@ -0,0 +1,34 @@
#lang scribble/manual
@(require "example-evaluator.rkt"
"defpredicate.rkt"
"module-title.rkt"
(for-label racket/base
generic-syntax-expanders/expanders
generic-syntax-expanders/expander-types
generic-syntax-expanders/define-expanders))
@module-title[generic-syntax-expanders/define-expanders]{Defining Generic Syntax Expanders}
This module provides a high-level API for creating generic
expanders for use with other macros.
@defform[(define-expander-type id)]{
Creates an expander type and binds several derived values
for working with the expander type:
@itemlist[
@item{@code{id-expander-type} - a new unique @racket[expander-type?]
bound at phase level 1}
@item{@code{make-id-expander} - a procedure bound at phase level 1
that accepts a transformer procedure and returns an @racket[expander?]
with @code{id-expander-type}}
@item{@code{id-expander?} - a predicate bound at phase level 1
recognizing expanders produced by @code{make-id-expander}}
@item{@code{define-id-expander?} - a syntactic form at phase level
0 that takes an identifier and a transformer procedure and binds the
identifier as a @code{id-expander?} for use in a transformer
environment}
@item{@code{expand-all-id-expanders} - a procedure bound at phase
level 1 that's equivalent to @racket[expand-all-expanders-of-type] with
the @code{id-expander-type} as the type argument}
]}

View File

@ -0,0 +1,20 @@
#lang scribble/manual
@(require "example-evaluator.rkt"
"module-title.rkt"
(for-label racket/base
lenses
generic-syntax-expanders/scoped-transformers
generic-syntax-expanders/define-scoped-transformers))
@module-title[generic-syntax-expanders/define-scoped-transformers]{Lens Scoped Syntax Transformers - Definition Form}
Syntax definition forms built on @racket[with-scoped-pre-transformer]
and friends.
@defform[(define-syntax-with-scoped-pre-transformers id
([stx-lens pre-transformer] ...)
transformer-expr)]{
Binds @racket[id] as a syntax transformer equivalent to
@racket[with-scoped-pre-transformers transformer-expr ([stx-lens pre-transformer] ...)].
}

View File

@ -0,0 +1,8 @@
#lang racket
(require scribble/manual)
(provide defpredicate)
(define-syntax-rule (defpredicate id pre-flow ...)
(defthing #:kind "predicate" id predicate/c pre-flow ...))

View File

@ -0,0 +1,11 @@
#lang racket
(require scribble/eval)
(provide package-examples)
(define generic-syntax-expanders-eval (make-base-eval))
(generic-syntax-expanders-eval '(require generic-syntax-expanders))
(define-syntax-rule (package-examples example-body ...)
(examples #:eval generic-syntax-expanders-eval example-body ...))

View File

@ -0,0 +1,56 @@
#lang scribble/manual
@(require "example-evaluator.rkt"
"defpredicate.rkt"
"module-title.rkt"
(for-label racket/base
generic-syntax-expanders/expanders
generic-syntax-expanders/expander-types))
@module-title[generic-syntax-expanders/expander-types]{Expander Types}
Under the hood, each generic expander defined with this library has
an associated @italic{expander type}. Syntax transformers built
with this library examine this type to determine whether or not
they should expand them.
@defpredicate[expander-type?]{
A predicate for values produced by @racket[make-expander-type] and
variants.
@package-examples[
(expander-type? (make-expander-type))
(expander-type? 'foo)
]}
@defproc[(make-expander-type) expander-type?]{
Creates a unique @racket[expander-type?] for use in defining a new
kind of generic expander.
@package-examples[
(make-expander-type)
]}
@defproc[(make-union-expander-type [type expander-type?] ...+) expander-type?]{
Creates a union @racket[expander-type?]. This union type includes
all of the given types, as well as any union type of a subset of
the given types.
@package-examples[
(make-union-expander-type (make-expander-type) (make-expander-type))
]}
@defproc[(expander-type-includes? [type-1 expander-type?] [type-2 expander-type?]) boolean?]{
Returns @racket[#t] if the two types are either identical, or if either
type is a union type that contains the other, or if both types are
union types and contain a nonempty intersection. Returns @racket[#f]
otherwise.
@package-examples[
(define A (make-expander-type))
(define B (make-expander-type))
(define C (make-expander-type))
(expander-type-includes? A A)
(expander-type-includes? B C)
(define AB (make-union-expander-type A B))
(define BC (make-union-expander-type B C))
(expander-type-includes? AB A)
(expander-type-includes? AB C)
(expander-type-includes? AB BC)
]}

View File

@ -0,0 +1,45 @@
#lang scribble/manual
@(require "example-evaluator.rkt"
"defpredicate.rkt"
"module-title.rkt"
(for-label racket/base
generic-syntax-expanders/expanders
generic-syntax-expanders/expander-types))
@module-title[generic-syntax-expanders/expanders]{Expanders And Transformers}
Generic expanders are implemented as values of the @racket[expander?] struct
bound with @racket[define-syntax], that store both a type and a transformer
procedure. Future versions of this library may support storing an additional
transformer for use outside expander-contexts in normal syntax parsing. This
could be used for better error messages, or for an expander meant to have
meaning in both a particularly typed expansion context and a normal expression
expansion context.
@defstruct[expander ([type expander-type?] [transformer (-> syntax? syntax?)])]{
A structure type for generic syntax expanders. A generic syntax expander
has an associated @italic{type} and @italic{transformer}. The transformer
can be any arbitrary function that accepts a syntax object in the same
manner a transformer given to @racket[define-syntax] would behave.
}
@defproc[(expander-of-type? [type expander-type?] [expander expander?]) boolean?]{
Returns @racket[#t] if the @racket[expander] has type @racket[type],
according to the semantics of @racket[expander-type-includes?], and
returns @racket[#f] otherwise.
@package-examples[
(define A (make-expander-type))
(define exp (expander A (λ (stx) stx)))
(expander-of-type? A exp)
]}
@defproc[(expand-stx-tree-with-expanders-of-type [type expander-type?] [syntax syntax?]) syntax?]{
Recursively searches through @racket[syntax] for identifiers bound to
generic syntax expanders of the given type. When an expander is found,
its transformer is called with the given syntax value of its location
in the tree. The returned syntax object with have all expanders of the
given type fully expanded, but nothing else will be expanded. Due to
how expanders are bound to identifiers, this procedure can only be
called in a transformer environment.
}

View File

@ -0,0 +1,32 @@
#lang scribble/manual
@(require "example-evaluator.rkt"
"defpredicate.rkt"
"module-title.rkt"
"source-code.rkt"
(for-label racket/base
racket/match
generic-syntax-expanders))
@module-title[generic-syntax-expanders]{Generic Syntax Expanders}
@author[@author+email["Jack Firth" "jackhfirth@gmail.com"]]
This library provides forms to define @italic{generic syntax
expanders}. These are essentially macros that have no meaning
on their own, but other macros can be told to expand all
generic syntax expanders of some type in some portion of
their body before themselves expanding. This is similar to
how Racket's built in @racket[match] form has @italic{match
expanders}, which allows the grammar of the @racket[match]
form to be extended with custom match expanders using
@racket[define-match-expander]. This library generalizes
the concept, making complex macros more composable and
extensible.
@source-code{https://github.com/jackfirth/generic-syntax-expanders}
@include-section{expanders.scrbl}
@include-section{expander-types.scrbl}
@include-section{define-expanders.scrbl}
@include-section{scoped-transformers.scrbl}
@include-section{define-scoped-transformers.scrbl}

View File

@ -0,0 +1,10 @@
#lang racket
(require scribble/manual)
(provide module-title)
(define-syntax-rule (module-title id title-text pre-flow ...)
(begin
(title title-text)
(defmodule id pre-flow ...)))

View File

@ -0,0 +1,44 @@
#lang scribble/manual
@(require "example-evaluator.rkt"
"module-title.rkt"
(for-label racket/base
lenses
generic-syntax-expanders/define-expanders
generic-syntax-expanders/scoped-transformers))
@module-title[generic-syntax-expanders/scoped-transformers]{Lens Scoped Syntax Transformers}
This module uses the @racket[lenses] package to create syntax transformers
that affect only some small subpiece of a syntax object and compose them
with other transformers. This allows for the creation of a macro that cedes
control to other macros to pre-expand parts of its body before the macro
expands. Combined with the syntax transformer produced by @racket[define-expander-type],
this makes it easy to define a macro that expands all instances of a generic
expander type in a specific subpiece of its body, turning it into an
extensible macro.
@defproc[((with-scoped-pre-transformer
[transformer (-> syntax? syntax?)]
[stx-lens (lens/c syntax? syntax?)]
[pre-transformer (-> syntax? syntax?)])
[stx syntax?])
syntax?]{
Transformers @racket[stx] in two passes. First, the piece of @racket[stx]
that @racket[stx-lens] views is transformed with @racket[pre-transformer].
Then, the entire resulting syntax object is transformed with @racket[transformer].
}
@defproc[((with-scoped-pre-transformers
[transformer (-> syntax? syntax?)]
[pre-transformer-lens-pairs
(listof (list/c (lens/c syntax? syntax?)
(-> syntax? syntax?)))])
[stx syntax?])
syntax?]{
Similar to @racket[with-scoped-pre-transformer]. Given @racket[pre-transformer-lens-pairs],
a list of pairs of lenses and transformers, @racket[transformer] is wrapped
with @racket[with-scoped-pre-transformer] with the pair's pre-transformer
and lens. The last pair in @racket[pre-transformer-lens-pairs] is applied
to @racket[stx] first.
}

View File

@ -0,0 +1,9 @@
#lang racket
(require scribble/manual
scribble/text)
(provide source-code)
(define (source-code link)
(list "Source code can be found at " (url link)))

View File

@ -8,7 +8,7 @@
(contract-out (contract-out
[expander-type? predicate/c] [expander-type? predicate/c]
[make-expander-type (-> expander-type?)] [make-expander-type (-> expander-type?)]
[make-union-expander-type (->* (expander-type?) () #:rest expander-type? expander-type?)] [make-union-expander-type (->* (expander-type?) () #:rest (listof expander-type?) expander-type?)]
[expander-type-includes? (-> expander-type? expander-type? boolean?)])) [expander-type-includes? (-> expander-type? expander-type? boolean?)]))
(define (type-includes? symtree-type1 symtree-type2) (define (type-includes? symtree-type1 symtree-type2)

View File

@ -9,7 +9,6 @@
(provide (struct-out expander) (provide (struct-out expander)
(contract-out (contract-out
[expander-of-type? (-> expander-type? expander? boolean?)] [expander-of-type? (-> expander-type? expander? boolean?)]
[expander-stx-of-type? (-> expander-type? expander-stx? boolean?)]
[expand-syntax-tree-with-expanders-of-type (-> expander-type? syntax? syntax?)])) [expand-syntax-tree-with-expanders-of-type (-> expander-type? syntax? syntax?)]))
(define (maybe-syntax-local-value stx) (define (maybe-syntax-local-value stx)

View File

@ -1,4 +1,4 @@
#lang info #lang info
(define name "package-name") (define name "generic-syntax-expanders")
(define scribblings '(("package-name.scrbl" ()))) (define scribblings '(("docs/generic-syntax-expanders.scrbl" ())))

View File

@ -2,5 +2,8 @@
(require mischief) (require mischief)
(require/provide "define-expanders.rkt" (require/provide "expander-types.rkt"
"scoped-transformers.rkt") "expanders.rkt"
"define-expanders.rkt"
"scoped-transformers.rkt"
"define-scoped-transformers.rkt")

View File

@ -1,28 +1,16 @@
#lang racket #lang racket
(require (for-syntax racket/match (require racket/match
lenses)) lenses)
(provide define-syntax-with-scoped-pre-transformers (provide with-scoped-pre-transformer
(for-syntax with-scoped-pre-transformer with-scoped-pre-transformers)
with-scoped-pre-transformers))
(define-for-syntax ((with-scoped-pre-transformer transformer stx-lens pre-transformer) stx) (define ((with-scoped-pre-transformer transformer stx-lens pre-transformer) stx)
(transformer (lens-transform stx-lens pre-transformer stx))) (transformer (lens-transform stx-lens pre-transformer stx)))
(define-for-syntax (with-scoped-pre-transformers transformer pre-transformer-lens-pairs) (define (with-scoped-pre-transformers transformer pre-transformer-lens-pairs)
(match pre-transformer-lens-pairs (match pre-transformer-lens-pairs
['() transformer] ['() transformer]
[(list (list stx-lens pre-transformer) rest ...) [(list (list stx-lens pre-transformer) rest ...)
(with-scoped-pre-transformers (with-scoped-pre-transformer stx-lens transformer) rest)])) (with-scoped-pre-transformers (with-scoped-pre-transformer stx-lens transformer) rest)]))
(define-syntax define-syntax-with-scoped-pre-transformers
(syntax-rules ()
[(_ name ([stx-lens pre-transformer] ...) transformer-expr)
(define-syntax name
(with-scoped-pre-transformers transformer-expr
(list (list stx-lens pre-transformer) ...)))]
[(_ (name stx) ([stx-lens pre-transformer] ...) transformer-body ...)
(define-syntax-with-scoped-pre-transformers name
([stx-lens pre-transformer] ...)
(lambda (stx) transformer-body ...))]))

View File

@ -5,4 +5,4 @@
(define build-deps '("cover" (define build-deps '("cover"
"scribble-lib" "scribble-lib"
"rackunit-lib" "rackunit-lib"
"racket-doc")) "racket-doc"))