diff --git a/.gitignore b/.gitignore index 30295e2..c1420c6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ -compiled/* +**/compiled *.bak *.html *.css *.js -*.2 \ No newline at end of file +*.2 +*~ + diff --git a/generic-syntax-expanders/define-expanders.rkt b/generic-syntax-expanders/define-expanders.rkt index e58378b..5277e6a 100644 --- a/generic-syntax-expanders/define-expanders.rkt +++ b/generic-syntax-expanders/define-expanders.rkt @@ -13,7 +13,6 @@ (with-derived-ids #'name ([?-expander-type "~a-expander-type"] [make-?-expander "make-~a-expander"] [?-expander? "~a-expander?"] - [?-expander-stx? "~a-expander-stx?"] [define-?-expander "define-~a-expander"] [expand-all-?-expanders "expand-all-~a-expanders"]) #'(begin @@ -22,8 +21,6 @@ (expander ?-expander-type transformer)) (define-for-syntax (?-expander? 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 expander-name (make-?-expander transformer))) (define-for-syntax (expand-all-?-expanders stx) diff --git a/generic-syntax-expanders/define-scoped-transformers.rkt b/generic-syntax-expanders/define-scoped-transformers.rkt new file mode 100644 index 0000000..5e30c35 --- /dev/null +++ b/generic-syntax-expanders/define-scoped-transformers.rkt @@ -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 ...))])) diff --git a/generic-syntax-expanders/docs/define-expanders.scrbl b/generic-syntax-expanders/docs/define-expanders.scrbl new file mode 100644 index 0000000..c5ee2f1 --- /dev/null +++ b/generic-syntax-expanders/docs/define-expanders.scrbl @@ -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} +]} diff --git a/generic-syntax-expanders/docs/define-scoped-transformers.scrbl b/generic-syntax-expanders/docs/define-scoped-transformers.scrbl new file mode 100644 index 0000000..dba62ed --- /dev/null +++ b/generic-syntax-expanders/docs/define-scoped-transformers.scrbl @@ -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] ...)]. +} diff --git a/generic-syntax-expanders/docs/defpredicate.rkt b/generic-syntax-expanders/docs/defpredicate.rkt new file mode 100644 index 0000000..b3a4736 --- /dev/null +++ b/generic-syntax-expanders/docs/defpredicate.rkt @@ -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 ...)) diff --git a/generic-syntax-expanders/docs/example-evaluator.rkt b/generic-syntax-expanders/docs/example-evaluator.rkt new file mode 100644 index 0000000..5f20ad5 --- /dev/null +++ b/generic-syntax-expanders/docs/example-evaluator.rkt @@ -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 ...)) \ No newline at end of file diff --git a/generic-syntax-expanders/docs/expander-types.scrbl b/generic-syntax-expanders/docs/expander-types.scrbl new file mode 100644 index 0000000..fd7aa1b --- /dev/null +++ b/generic-syntax-expanders/docs/expander-types.scrbl @@ -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) +]} diff --git a/generic-syntax-expanders/docs/expanders.scrbl b/generic-syntax-expanders/docs/expanders.scrbl new file mode 100644 index 0000000..aeeb5c1 --- /dev/null +++ b/generic-syntax-expanders/docs/expanders.scrbl @@ -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. +} diff --git a/generic-syntax-expanders/docs/generic-syntax-expanders.scrbl b/generic-syntax-expanders/docs/generic-syntax-expanders.scrbl new file mode 100644 index 0000000..44dfffd --- /dev/null +++ b/generic-syntax-expanders/docs/generic-syntax-expanders.scrbl @@ -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} diff --git a/generic-syntax-expanders/docs/module-title.rkt b/generic-syntax-expanders/docs/module-title.rkt new file mode 100644 index 0000000..5a08f99 --- /dev/null +++ b/generic-syntax-expanders/docs/module-title.rkt @@ -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 ...))) diff --git a/generic-syntax-expanders/docs/scoped-transformers.scrbl b/generic-syntax-expanders/docs/scoped-transformers.scrbl new file mode 100644 index 0000000..d872a2a --- /dev/null +++ b/generic-syntax-expanders/docs/scoped-transformers.scrbl @@ -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. +} diff --git a/generic-syntax-expanders/docs/source-code.rkt b/generic-syntax-expanders/docs/source-code.rkt new file mode 100644 index 0000000..993e3bf --- /dev/null +++ b/generic-syntax-expanders/docs/source-code.rkt @@ -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))) diff --git a/generic-syntax-expanders/expander-types.rkt b/generic-syntax-expanders/expander-types.rkt index 8018bd3..ddf9846 100644 --- a/generic-syntax-expanders/expander-types.rkt +++ b/generic-syntax-expanders/expander-types.rkt @@ -8,7 +8,7 @@ (contract-out [expander-type? predicate/c] [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?)])) (define (type-includes? symtree-type1 symtree-type2) diff --git a/generic-syntax-expanders/expanders.rkt b/generic-syntax-expanders/expanders.rkt index 2153b1d..303670c 100644 --- a/generic-syntax-expanders/expanders.rkt +++ b/generic-syntax-expanders/expanders.rkt @@ -9,7 +9,6 @@ (provide (struct-out expander) (contract-out [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?)])) (define (maybe-syntax-local-value stx) diff --git a/generic-syntax-expanders/info.rkt b/generic-syntax-expanders/info.rkt index 3b59693..97e1aae 100644 --- a/generic-syntax-expanders/info.rkt +++ b/generic-syntax-expanders/info.rkt @@ -1,4 +1,4 @@ #lang info -(define name "package-name") -(define scribblings '(("package-name.scrbl" ()))) \ No newline at end of file +(define name "generic-syntax-expanders") +(define scribblings '(("docs/generic-syntax-expanders.scrbl" ()))) \ No newline at end of file diff --git a/generic-syntax-expanders/main.rkt b/generic-syntax-expanders/main.rkt index e0ca77d..4b433fc 100644 --- a/generic-syntax-expanders/main.rkt +++ b/generic-syntax-expanders/main.rkt @@ -2,5 +2,8 @@ (require mischief) -(require/provide "define-expanders.rkt" - "scoped-transformers.rkt") +(require/provide "expander-types.rkt" + "expanders.rkt" + "define-expanders.rkt" + "scoped-transformers.rkt" + "define-scoped-transformers.rkt") diff --git a/generic-syntax-expanders/scoped-transformers.rkt b/generic-syntax-expanders/scoped-transformers.rkt index 7553f77..6f23fa3 100644 --- a/generic-syntax-expanders/scoped-transformers.rkt +++ b/generic-syntax-expanders/scoped-transformers.rkt @@ -1,28 +1,16 @@ #lang racket -(require (for-syntax racket/match - lenses)) +(require racket/match + lenses) -(provide define-syntax-with-scoped-pre-transformers - (for-syntax with-scoped-pre-transformer - with-scoped-pre-transformers)) +(provide with-scoped-pre-transformer + 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))) -(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 ['() transformer] [(list (list stx-lens pre-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 ...))])) diff --git a/info.rkt b/info.rkt index de3d668..74dfefe 100644 --- a/info.rkt +++ b/info.rkt @@ -5,4 +5,4 @@ (define build-deps '("cover" "scribble-lib" "rackunit-lib" - "racket-doc")) \ No newline at end of file + "racket-doc"))