From f2204710fa66b3f719b5fec92030fef1069294f1 Mon Sep 17 00:00:00 2001 From: Ryan Culpepper Date: Fri, 13 Feb 2009 03:23:07 +0000 Subject: [PATCH] stxclass: reorganized docs svn: r13544 --- collects/stxclass/info.ss | 3 +- collects/stxclass/main.ss | 6 +- collects/stxclass/private/lib.ss | 18 +- collects/stxclass/private/rep.ss | 39 +- collects/stxclass/private/runtime.ss | 2 + collects/stxclass/private/sc.ss | 32 +- collects/stxclass/scribblings/library.scrbl | 103 +++ .../stxclass/scribblings/parsing-syntax.scrbl | 239 ++++++ collects/stxclass/scribblings/stxclass.scrbl | 88 ++ .../stxclass/scribblings/syntax-classes.scrbl | 210 +++++ collects/stxclass/scribblings/util.scrbl | 232 ++++++ collects/stxclass/stxclass.scrbl | 774 ------------------ collects/tests/stxclass/stxclass.ss | 26 + 13 files changed, 961 insertions(+), 811 deletions(-) create mode 100644 collects/stxclass/scribblings/library.scrbl create mode 100644 collects/stxclass/scribblings/parsing-syntax.scrbl create mode 100644 collects/stxclass/scribblings/stxclass.scrbl create mode 100644 collects/stxclass/scribblings/syntax-classes.scrbl create mode 100644 collects/stxclass/scribblings/util.scrbl delete mode 100644 collects/stxclass/stxclass.scrbl diff --git a/collects/stxclass/info.ss b/collects/stxclass/info.ss index 4a512b658f..b971f72ceb 100644 --- a/collects/stxclass/info.ss +++ b/collects/stxclass/info.ss @@ -1,3 +1,4 @@ #lang setup/infotab -(define scribblings '(("stxclass.scrbl"))) +(define scribblings + '(("scribblings/stxclass.scrbl" (multi-page) (experimental)))) diff --git a/collects/stxclass/main.ss b/collects/stxclass/main.ss index 3779837637..ad37c75457 100644 --- a/collects/stxclass/main.ss +++ b/collects/stxclass/main.ss @@ -7,6 +7,7 @@ define-basic-syntax-class define-basic-syntax-class* pattern + basic-syntax-class syntax-parse syntax-parser @@ -16,4 +17,7 @@ current-expression current-macro-name - (all-from-out "private/lib.ss")) + (all-from-out "private/lib.ss") + + (rename-out [parse-sc syntax-class-parse] + [attrs-of syntax-class-attributes])) diff --git a/collects/stxclass/private/lib.ss b/collects/stxclass/private/lib.ss index 4a71dae7f0..6497a307e4 100644 --- a/collects/stxclass/private/lib.ss +++ b/collects/stxclass/private/lib.ss @@ -53,16 +53,6 @@ (pattern :define-values-form) (pattern :define-syntaxes-form)) -(define-basic-syntax-class static - ([value 0]) - (lambda (x) - (if (identifier? x) - (let/ec escape - (define (bad) (escape #f)) - (let ([value (syntax-local-value x bad)]) - (list (syntax-e x) value))) - #f))) - (define-syntax-class (static-of name pred) #:description name #:attributes ([value 0]) @@ -73,9 +63,15 @@ (if (identifier? x) (let ([value (syntax-local-value x bad)]) (unless (pred value) (bad)) - (list x value)) + (list value)) (bad)))))) +(define-syntax-class static + #:attributes (value) + (pattern x + #:declare x (static-of "static" (lambda _ #t)) + #:with value #'x.value)) + (define-basic-syntax-class struct-name ([descriptor 0] [constructor 0] diff --git a/collects/stxclass/private/rep.ss b/collects/stxclass/private/rep.ss index 204e8268db..179648975e 100644 --- a/collects/stxclass/private/rep.ss +++ b/collects/stxclass/private/rep.ss @@ -1,6 +1,6 @@ #lang scheme/base -(require (for-template "runtime.ss") - (for-template scheme/base) +(require (for-template scheme/base) + (for-template "runtime.ss") scheme/contract scheme/match syntax/boundmap @@ -59,14 +59,29 @@ (define transparent? (and trans0 #t)) (define attributes (and attrs0 (caddr attrs0))) - (define (parse-rhs*-basic rest) - (syntax-case rest (basic-syntax-class) - [((basic-syntax-class parser-expr)) - (make rhs:basic ctx - (or attributes null) - transparent? - description - #'parser-expr)])) + (define (parse-rhs*-basic rhss) + (syntax-case rhss (basic-syntax-class) + [((basic-syntax-class . rest)) + (let-values ([(basic-chunks rest) + (chunk-kw-seq/no-dups #'rest basic-rhs-directive-table + #:context (stx-car rhss))]) + (syntax-case rest () + [(parser-expr) + (make rhs:basic ctx + (or attributes null) + transparent? + description + (if (assq '#:transforming basic-chunks) + #'parser-expr + #`(let ([parser parser-expr]) + (lambda (x . args) + (let ([result (apply parser x args)]) + (if (ok? result) + (cons x result) + result))))))] + [_ + (wrong-syntax (stx-car rhss) + "expected parser expression")]))])) (define (parse-rhs*-patterns rest) (define (gather-patterns stx) @@ -359,3 +374,7 @@ (list '#:description values) (list '#:transparent) (list '#:attributes check-attr-arity-list))) + +;; basic-rhs-directive-table +(define basic-rhs-directive-table + (list (list '#:transforming))) diff --git a/collects/stxclass/private/runtime.ss b/collects/stxclass/private/runtime.ss index 54c409eb7e..bb1f82b5a5 100644 --- a/collects/stxclass/private/runtime.ss +++ b/collects/stxclass/private/runtime.ss @@ -7,6 +7,7 @@ (for-syntax "rep-data.ss") (for-syntax "../util/error.ss")) (provide pattern + basic-syntax-class ...* with-enclosing-fail @@ -35,6 +36,7 @@ (raise-syntax-error #f "keyword used out of context" stx)))) (define-keyword pattern) +(define-keyword basic-syntax-class) (define-keyword ...*) (define-keyword ...**) diff --git a/collects/stxclass/private/sc.ss b/collects/stxclass/private/sc.ss index 463ff1dc6a..d30867bce7 100644 --- a/collects/stxclass/private/sc.ss +++ b/collects/stxclass/private/sc.ss @@ -25,6 +25,7 @@ with-patterns pattern + basic-syntax-class ...* (struct-out failed) @@ -118,6 +119,7 @@ (define-syntax-class (name arg ...) #:attributes ([attr-name attr-depth] ...) (basic-syntax-class + #:transforming (let ([name parser-expr]) name)))])) (define-syntax (rhs->parser+description stx) @@ -137,23 +139,25 @@ (define-syntax (parse-sc stx) (syntax-case stx () [(parse s x arg ...) - (let* ([stxclass (get-stxclass #'s)] - [attrs (flatten-sattrs (sc-attrs stxclass))]) - (with-syntax ([parser (sc-parser-name stxclass)] - [(name ...) (map attr-name attrs)] - [(depth ...) (map attr-depth attrs)]) - #'(let ([raw (parser x arg ...)]) - (if (ok? raw) - (map vector '(name ...) '(depth ...) (cdr raw)) - raw))))])) + (parameterize ((current-syntax-context stx)) + (let* ([stxclass (get-stxclass #'s)] + [attrs (flatten-sattrs (sc-attrs stxclass))]) + (with-syntax ([parser (sc-parser-name stxclass)] + [(name ...) (map attr-name attrs)] + [(depth ...) (map attr-depth attrs)]) + #'(let ([raw (parser x arg ...)]) + (if (ok? raw) + (map vector '(name ...) '(depth ...) (cdr raw)) + raw)))))])) (define-syntax (attrs-of stx) (syntax-case stx () [(attrs-of s) - (let ([attrs (flatten-sattrs (sc-attrs (get-stxclass #'s)))]) - (with-syntax ([(a ...) (map attr-name attrs)] - [(depth ...) (map attr-depth attrs)]) - #'(quote ((a depth) ...))))])) + (parameterize ((current-syntax-context stx)) + (let ([attrs (flatten-sattrs (sc-attrs (get-stxclass #'s)))]) + (with-syntax ([(a ...) (map attr-name attrs)] + [(depth ...) (map attr-depth attrs)]) + #'(quote ((a depth) ...)))))])) (define-syntax (debug-rhs stx) (syntax-case stx () @@ -186,7 +190,7 @@ (let ([fail (syntax-patterns-fail x)]) (parameterize ((current-expression (or (current-expression) x))) #,(parse:clauses #'clauses #'x #'fail))))))])) - + (define-syntax with-patterns (syntax-rules () [(with-patterns () . b) diff --git a/collects/stxclass/scribblings/library.scrbl b/collects/stxclass/scribblings/library.scrbl new file mode 100644 index 0000000000..e3add81914 --- /dev/null +++ b/collects/stxclass/scribblings/library.scrbl @@ -0,0 +1,103 @@ +#lang scribble/doc +@(require scribble/manual + scribble/struct + scribble/decode + (for-label scheme/base + scheme/contract + stxclass + stxclass/util)) + +@(define ellipses @scheme[...]) +@(define (TODO . pre-flow) + (make-splice + (cons (bold "TODO: ") + (decode-content pre-flow)))) + +@title{Library syntax classes} +@declare-exporting[stxclass] + +@(define-syntax-rule (defstxclass name . pre-flows) + (defidform name . pre-flows)) + +@(define-syntax-rule (defstxclass* (name arg ...) . pre-flows) + (defform (name arg ...) . pre-flows)) + +@defstxclass[expr]{ + +Matches anything except a keyword literal (to distinguish expressions +from the start of a keyword argument sequence). Does not expand or +otherwise inspect the term. + +} + +@deftogether[( +@defstxclass[identifier] +@defstxclass[boolean] +@defstxclass[str] +@defstxclass[char] +@defstxclass[keyword] +@defstxclass[number] +@defstxclass[integer] +@defstxclass[exact-integer] +@defstxclass[exact-nonnegative-integer] +@defstxclass[exact-positive-integer])]{ + +Match syntax satisfying the corresponding predicates. + +} + +@defstxclass[id]{ Alias for @scheme[identifier]. } +@defstxclass[nat]{ Alias for @scheme[exact-nonnegative-integer]. } + +The following syntax classes mirror parts of the macro API. They may +only be used during transformation (when @scheme[syntax-transforming?] +returns true). Otherwise they may raise an error. + +@defstxclass[static]{ + +Matches identifiers that are bound in the syntactic environment to +static information (see @scheme[syntax-local-value]). Attribute +@scheme[_value] contains the value the name is bound to. + +} + +@defform[(static-of description predicate)]{ + +Refines @scheme[static]: matches identifiers that are bound in the +syntactic environment to static information satisfying the given +@scheme[predicate]. Attribute @scheme[_value] contains the value the +name is bound to. The @scheme[description] argument is used for error +reporting. + +} + +@;{ +@defstxclass[struct-name]{ + +Matches identifiers bound to static struct information. Attributes are +@scheme[_descriptor], @scheme[_constructor], @scheme[_predicate], +@scheme[(_accessor ...)], @scheme[_super], and @scheme[_complete?]. + +} +} +@;{ +@defstxclass[expr/local-expand]{ + +Matches any term and @scheme[local-expand]s it as an expression with +an empty stop list. Attribute @scheme[_expanded] is the expanded form. + +} + +@defstxclass[expr/head-local-expand] +@defstxclass[block/head-local-expand] +@defstxclass[internal-definitions] +} + +@;{ +@defform[(expr/c contract-expr-stx)]{ + +Accepts any term and returns as the match that term wrapped in a +@scheme[contract] expression enforcing @scheme[contract-expr-stx]. + +} +} diff --git a/collects/stxclass/scribblings/parsing-syntax.scrbl b/collects/stxclass/scribblings/parsing-syntax.scrbl new file mode 100644 index 0000000000..8a27fff26f --- /dev/null +++ b/collects/stxclass/scribblings/parsing-syntax.scrbl @@ -0,0 +1,239 @@ +#lang scribble/doc +@(require scribble/manual + scribble/struct + scribble/decode + (for-label scheme/base + scheme/contract + stxclass + stxclass/util)) + +@(define ellipses @scheme[...]) +@(define (TODO . pre-flow) + (make-splice + (cons (bold "TODO: ") + (decode-content pre-flow)))) + +@title{Parsing Syntax} +@declare-exporting[stxclass] + +This section describes @schememodname[stxclass]'s facilities for +parsing syntax. + +@defform/subs[(syntax-parse stx-expr maybe-literals clause ...) + ([maybe-literals code:blank + (code:line #:literals (literal-id ...))] + [clause (syntax-pattern pattern-directive ... expr)])]{ + +Evaluates @scheme[stx-expr], which should produce a syntax object, and +matches it against the patterns in order. If some pattern matches, its +pattern variables are bound to the corresponding subterms of the +syntax object and that clause's side conditions and @scheme[expr] is +evaluated. The result is the result of @scheme[expr]. + +If the syntax object fails to match any of the patterns (or all +matches fail the corresponding clauses' side conditions), a syntax +error is raised. The syntax error indicates the first specific subterm +for which no pattern matches. + +@TODO{Allow literal declarations of form @scheme[(_internal-name +_external-name)].} +} + +@defform[(syntax-parser maybe-literals clause ...)]{ + +Like @scheme[syntax-parse], but produces a matching procedure. The +procedure accepts a single argument, which should be a syntax object. + +} + +The grammar of patterns accepted by @scheme[syntax-parse] and +@scheme[syntax-parser] follows: + +@schemegrammar*[#:literals (_ ...*) + [syntax-pattern + pvar-id + pvar-id:syntax-class-id + literal-id + atomic-datum + (syntax-pattern . syntax-pattern) + (syntax-pattern #,ellipses . syntax-pattern) + ((head ...+) ...* . syntax-pattern)] + [pvar-id + _ + id]] + +Here are the variants of @scheme[syntax-pattern]: + +@specsubform[pvar-id]{ + +Matches anything. The pattern variable is bound to the matched +subterm, unless the pattern variable is the wildcard (@scheme[_]), in +which case no binding occurs. + +} +@specsubform[pvar-id:syntax-class-id]{ + +Matches only subterms specified by the @scheme[_syntax-class-id]. The +syntax class's attributes are computed for the subterm and bound to +the pattern variables formed by prefixing @scheme[_pvar-id.] to the +name of the attribute. @scheme[_pvar-id] is typically bound to the +matched subterm, but the syntax class can substitute a transformed +subterm instead. + +@;{(for example, @scheme[expr/c] wraps the matched +subterm in a @scheme[contract] expression).} + +If @scheme[_pvar-id] is @scheme[_], no pattern variables are bound. + +} +@specsubform[literal-id]{ + +An identifier that appears in the literals list is not a pattern +variable; instead, it is a literal that matches any identifier +@scheme[free-identifier=?] to it. + +} +@specsubform[atomic-datum]{ + +The empty list, numbers, strings, booleans, and keywords match as +literals. + +} +@specsubform[(syntax-pattern . syntax-pattern)]{ + +Matches a syntax pair whose head matches the first pattern and whose +tail matches the second. + +} +@;{ +@specsubform[(syntax-splice-pattern . syntax-pattern)]{ + +Matches a syntax object which consists of any sequence of syntax +objects matching the splice pattern followed by a tail matching the +given tail pattern. + +} +} + +@specsubform[(syntax-pattern #,ellipses . syntax-pattern)]{ + +Matches a sequence of the first pattern ending in a tail matching the +second pattern. + +That is, the sequence pattern matches either the second pattern (which +need not be a list) or a pair whose head matches the first pattern and +whose tail recursively matches the whole sequence pattern. + +} +@specsubform/subs[#:literals (...*) + ((head ...+) ...* . syntax-pattern) + ([head + (code:line (syntax-pattern ...+) head-directive ...)] + [head-directive + (code:line #:min min-reps) + (code:line #:max max-reps) + (code:line #:mand) + #| (code:line #:opt) + (code:line #:occurs occurs-pvar-id) + (code:line #:default default-form) + |#])]{ + +Matches a sequence of any combination of the heads ending in a tail +matching the final pattern. The match is subject to constraints +specified on the heads. + +@specsubform[(code:line #:min min-reps)]{ + +Requires at least @scheme[min-reps] occurrences of the preceding head +to match. @scheme[min-reps] must be a literal exact nonnegative +integer. + +} +@specsubform[(code:line #:max max-reps)]{ + +Requires that no more than @scheme[max-reps] occurrences of the +preceeding head to match. @scheme[max-reps] must be a literal exact +nonnegative integer, and it must be greater than or equal to +@scheme[min-reps]. + +} +@specsubform[#:mand]{ + +Requires that the preceding head occur exactly once. Pattern variables +in the preceding head are not bound at a higher ellipsis nesting +depth. + +} +@;{ +@specsubform[#:opt]{ + +(Probably a bad idea.) + +} +} +} +@;{ +The variants of @scheme[_syntax-splice-pattern] follow: + +@specsubform[pvar-id:syntax-splice-class-id]{ + +Matches a sequence of syntax objects described by +@scheme[_syntax-splice-class-id]. + +The name @scheme[_pvar-id] is bound, but not allowed within +expressions or @scheme[syntax] templates (since it does not refer to a +particular syntax object). Only the prefixed attributes of the splice +class are usable. +} +} + +Both @scheme[syntax-parse] and @scheme[syntax-parser] support +directives for annotating the pattern and specifying side +conditions. The grammar for pattern directives follows: + +@schemegrammar[pattern-directive + (code:line #:declare pattern-id syntax-class-id) + (code:line #:declare pattern-id (syntax-class-id expr ...)) + (code:line #:with syntax-pattern expr) + (code:line #:when expr)] + +@specsubform[(code:line #:declare pvar-id syntax-class-id)] +@specsubform[(code:line #:declare pvar-id (syntax-class-id expr ...))]{ + +The first form is equivalent to using the +@scheme[_pvar-id:syntax-class-id] form in the pattern (but it is +illegal to use both for a single pattern variable). The +@scheme[#:declare] form may be preferred when writing macro-defining +macros or to avoid dealing with structured identifiers. + +The second form allows the use of parameterized syntax classes, which +cannot be expressed using the ``colon'' notation. The @scheme[expr]s +are evaluated outside the scope of the pattern variable bindings. + +} +@specsubform[(code:line #:with syntax-pattern expr)]{ + +Evaluates the @scheme[expr] in the context of all previous pattern +variable bindings and matches it against the pattern. If the match +succeeds, the new pattern variables are added to the environment for +the evaluation of subsequent side conditions. If the @scheme[#:with] +match fails, the matching process backtracks. Since a syntax object +may match a pattern in several ways, backtracking may cause the same +clause to be tried multiple times before the next clause is reached. + +} +@specsubform[(code:line #:when expr)]{ + +Evaluates the @scheme[expr] in the context of all previous pattern +variable bindings. If it produces a false value, the matching process +backtracks as described above; otherwise, it continues. + +} + +@defidform[...*]{ + +Keyword recognized by @scheme[syntax-parse] etc as notation for +generalized sequences. It may not be used as an expression. + +} + diff --git a/collects/stxclass/scribblings/stxclass.scrbl b/collects/stxclass/scribblings/stxclass.scrbl new file mode 100644 index 0000000000..a8902fb591 --- /dev/null +++ b/collects/stxclass/scribblings/stxclass.scrbl @@ -0,0 +1,88 @@ +#lang scribble/doc +@(require scribble/manual + scribble/struct + scribble/decode + (for-label scheme/base + scheme/contract + stxclass + stxclass/util)) + +@(define ellipses @scheme[...]) +@(define (TODO . pre-flow) + (make-splice + (cons (bold "TODO: ") + (decode-content pre-flow)))) + +@title{Parsing Syntax and Syntax Classes} + +@bold{Warning: This library is still very volatile! Its interface and +behavior are subject to frequent change. I highly recommend that you +avoid creating PLaneT packages that depend on this library.} + +The @schememodname[stxclass] library provides a framework for +describing and parsing syntax. Using @schememodname[stxclass], macro +writers can define new syntactic categories, specify their legal +syntax, and use them to write clear, concise, and robust macros. + +To load the library: +@defmodule[stxclass] + +@;{The first section is an overview with examples that illustrate +@schememodname[stxclass] features.} + +The following sections are a reference for @schememodname[stxclass] +features. + +@include-section["parsing-syntax.scrbl"] +@include-section["syntax-classes.scrbl"] +@include-section["library.scrbl"] +@include-section["util.scrbl"] + +@local-table-of-contents[] + +@;{ + + +1 How to abstract over similar patterns: + +(syntax-parse stx #:literals (blah bleh blaz kwA kwX) + [(blah (bleh (kwX y z)) blaz) + ___] + [(blah (bleh (kwA (b c))) blaz) + ___]) + +=> + +(define-syntax-class common + #:attributes (inner) + #:literals (blah bleh blaz) + (pattern (blah (bleh inner) blaz))) +(syntax-parse stx #:literals (kwA kwX) + [c:common + #:with (kwX y z) #'c.inner + ___] + [c:common + #:with (kwA (b c)) #'c.inner + ___]) + + +OR => + +(define-syntax-class (common expected-kw) + #:attributes (inner) + #:literals (blah bleh blaz) + (pattern (blah (bleh (kw . inner)) blaz) + #:when (free-identifier=? #'kw expected-kw))) +(syntax-parse stx + [c + #:declare c (common #'kwX) + #:with (y z) #'c.inner + ___] + [c + #:declare c (common #'kwA) + #:with ((b c)) #'c.inner + ___]) + + +} + diff --git a/collects/stxclass/scribblings/syntax-classes.scrbl b/collects/stxclass/scribblings/syntax-classes.scrbl new file mode 100644 index 0000000000..8ac6aedff7 --- /dev/null +++ b/collects/stxclass/scribblings/syntax-classes.scrbl @@ -0,0 +1,210 @@ +#lang scribble/doc +@(require scribble/manual + scribble/struct + scribble/decode + (for-label scheme/base + scheme/contract + stxclass + stxclass/util)) + +@(define ellipses @scheme[...]) +@(define (TODO . pre-flow) + (make-splice + (cons (bold "TODO: ") + (decode-content pre-flow)))) + +@title{Syntax Classes} +@declare-exporting[stxclass] + +Syntax classes provide an abstraction mechanism for the specification +of syntax. Basic syntax classes include @scheme[identifier] and +@scheme[keyword]. More generally, a programmer can define a ``basic'' +syntax from an arbitrary predicate, although syntax classes thus +defined lose some of the benefits of declarative specification of +syntactic structure. + +Programmers can also compose basic syntax classes to build +specifications of more complex syntax, such as lists of distinct +identifiers and formal arguments with keywords. Macros that manipulate +the same syntactic structures can share syntax class definitions. The +structure of syntax classes and patterns also allows +@scheme[syntax-parse] to automatically generate error messages for +syntax errors. + +When a syntax class accepts (matches, includes) a syntax object, it +computes and provides attributes based on the contents of the matched +syntax. While the values of the attributes depend on the matched +syntax, the set of attributes and each attribute's ellipsis nesting +depth is fixed for each syntax class. + +@defform*/subs[#:literals (pattern basic-syntax-class) + [(define-syntax-class name-id stxclass-option ... + stxclass-body) + (define-syntax-class (name-id arg-id ...) stxclass-option ... + stxclass-body)] + ([stxclass-options + (code:line #:attributes (attr-arity-decl ...)) + (code:line #:description description) + (code:line #:transparent)] + [attr-arity-decl + attr-name-id + (attr-name-id depth)] + [stxclass-body + (code:line (pattern syntax-pattern stxclass-pattern-directive ...) ...+) + (code:line (basic-syntax-class parser-expr))])]{ + +Defines @scheme[name-id] as a syntax class. When the @scheme[arg-id]s +are present, they are bound as variables (not pattern variables) in +the body. The body of the syntax-class definition contains either one +@scheme[basic-syntax-class] clause or a non-empty sequence of +@scheme[pattern] clauses. + +@specsubform[(code:line #:attributes (attr-arity-decl ...))]{ + +Declares the attributes of the syntax class. An attribute arity +declaration consists of the attribute name and optionally its ellipsis +depth (zero if not explicitly specified). + +If the attributes are not explicitly listed, they are computed using +@techlink{attribute inference}. + +} + +@specsubform[(code:line #:description description)]{ + +The @scheme[description] argument is an expression (with the +syntax-class's parameters in scope) that should evaluate to a +string. It is used in error messages involving the syntax class. For +example, if a term is rejected by the syntax class, an error of the +form @scheme["expected "] may be generated. + +If absent, the name of the syntax class is used instead. + +} + +@specsubform[#:transparent]{ + +Indicates that errors may be reported with respect to the internal +structure of the syntax class. +} + +@specsubform/subs[#:literals (pattern) + (pattern syntax-pattern stxclass-pattern-directive ...) + ([stxclass-pattern-directive + pattern-directive + (code:line #:rename internal-id external-id)])]{ + +Accepts syntax matching the given pattern with the accompanying +pattern directives as in @scheme[syntax-parse]. + +The attributes of the pattern are the pattern variables within the +@scheme[pattern] form together with all pattern variables bound by +@scheme[#:with] clauses, including nested attributes produced by +syntax classes associated with the pattern variables. + +The name of an attribute is the symbolic name of the pattern variable, +except when the name is explicitly given via a @scheme[#:rename] +clause. + +@specsubform[(code:line #:rename internal-id external-id)]{ + +Exports the pattern variable binding named by @scheme[internal-id] as +the attribute named @scheme[external-id]. + +} +} + +@specsubform[#:literals (basic-syntax-class) + (basic-syntax-class parser-expr)]{ + +The @scheme[parser-expr] must evaluate to a procedure. This procedure +is used to parse or reject syntax objects. The arguments to the parser +procedure consist of the syntax object to parse followed by the +syntax-class parameterization arguments (the parameter names given at +the @scheme[define-syntax-class] level are not bound within the +@scheme[parser-expr]). To indicate success, the parser should return a +list of attribute values, one for each attribute listed. (For example, +a parser for a syntax class that defines no attributes returns the +empty list when it succeeds.) To indicate failure, the parser +procedure should return @scheme[#f]. + +The parser procedure should avoid side-effects, as they interfere with +the parsing process's backtracking and error reporting. + +@TODO{Add support for better error reporting within basic syntax +class.} + +} + +} + +@defidform[pattern]{ + +Keyword recognized by @scheme[define-syntax-class]. It may not be +used as an expression. +} +@defidform[basic-syntax-class]{ + +Keyword recognized by @scheme[define-syntax-class]. It may not be used +as an expression. + +} + +@section{Attributes} + +A syntax class has a set of @deftech{attribute}s. Each attribute has a +name, an ellipsis depth, and a set of nested attributes. When an +instance of the syntax class is parsed and bound to a pattern +variable, additional pattern variables are bound for each of the +syntax class's attributes. The name of these additional pattern +variables is the dotted concatenation of the the primary pattern +variable with the name of the attribute. + +For example, if pattern variable @scheme[p] is bound to an instance of +a syntax class with attribute @scheme[a], then the pattern variable +@scheme[p.a] is bound to the value of that attribute. The ellipsis +depth of @scheme[p.a] is the sum of the depths of @scheme[p] and +attribute @scheme[a]. + +If the attributes are not declared explicitly, they are computed via +@deftech{attribute inference}. For ``basic'' syntax classes, the +inferred attribute list is always empty. For compound syntax classes, +each @scheme[pattern] form is analyzed to determine its candiate +attributes. The attributes of the syntax class are the attributes +common to all of the variants (that is, the intersection of the +candidate attributes). An attribute must have the same ellipsis-depth +in each of the variants; otherwise, an error is raised. + +The candidate attributes of a @scheme[pattern] variant are the pattern +variables bound by the variant's pattern (including nested attributes +contributed by their associated syntax classes) together with the +pattern variables (and nested attributes) from @scheme[#:with] +clauses. + +For the purpose of attribute inference, recursive references to the +same syntax class and forward references to syntax classes not yet +defined do not contribute any nested attributes. This avoids various +problems in computing attributes, including infinitely nested +attributes. + +@section{Inspection tools} + +The following special forms are for debugging syntax classes. + +@defform[(syntax-class-attributes syntax-class-id)]{ + +Returns a list of the syntax class's attributes in flattened +form. Each attribute is listed by its name and ellipsis depth. + +} + +@defform[(syntax-class-parse syntax-class-id stx-expr arg-expr ...)]{ + +Runs the parser for the syntax class (parameterized by the +@scheme[arg-expr]s) on the syntax object produced by +@scheme[stx-expr]. On success, the result is a list of vectors +representing the attribute bindings of the syntax class. Each vector +contains the attribute name, depth, and associated value. On failure, +the result is some internal representation of the failure. + +} diff --git a/collects/stxclass/scribblings/util.scrbl b/collects/stxclass/scribblings/util.scrbl new file mode 100644 index 0000000000..ec6df7b123 --- /dev/null +++ b/collects/stxclass/scribblings/util.scrbl @@ -0,0 +1,232 @@ +#lang scribble/doc +@(require scribble/manual + scribble/struct + scribble/decode + (for-label scheme/base + scheme/contract + stxclass + stxclass/util)) + +@(define ellipses @scheme[...]) +@(define (TODO . pre-flow) + (make-splice + (cons (bold "TODO: ") + (decode-content pre-flow)))) + +@title{Utilities} + +The @schememodname[stxclass] collection includes several utility +modules. They are documented individually below. + +As a shortcut, the @schememodname[stxclass/util] module provides all +of the contents of the separate utility modules: + +@defmodule[stxclass/util] + +The contents of the utility modules are not provided by the main +@schememodname[stxclass] module. + +@section{Error reporting} + +@defmodule[stxclass/util/error] + +The @schememodname[scheme/base] and @schememodname[scheme] languages +provide the @scheme[raise-syntax-error] procedure for reporting syntax +errors. Using @scheme[raise-syntax-error] effectively requires passing +around either a symbol indicating the special form that signals the +error or else a ``contextual'' syntax object from which the special +form's name can be extracted. This library helps manage the contextual +syntax for reporting errors. + +@defparam[current-syntax-context stx (or/c syntax? false/c)]{ + +The current contextual syntax object, defaulting to @scheme[#f]. It +determines the special form name that prefixes syntax errors created +by @scheme[wrong-syntax], as follows: + +If it is a syntax object with a @scheme['report-error-as] syntax +property whose value is a symbol, then that symbol is used as the +special form name. Otherwise, the same rules apply as in +@scheme[raise-syntax-error]. + +} + +@defproc[(wrong-syntax [stx syntax?] [format-string string?] [v any/c] ...) + any]{ + +Raises a syntax error using the result of +@scheme[(current-syntax-context)] as the ``major'' syntax object and +the provided @scheme[stx] as the specific syntax object. (The latter, +@scheme[stx], is usually the one highlighted by DrScheme.) The error +message is constructed using the format string and arguments, and it +is prefixed with the special form name as described under +@scheme[current-syntax-context]. + +} + +A macro using this system might set the syntax context at the very +beginning of its transformation as follows: +@SCHEMEBLOCK[ +(define-syntax (my-macro stx) + (parameterize ((current-syntax-context stx)) + (syntax-case stx () + ___))) +] +Then any calls to @scheme[wrong-syntax] during the macro's +transformation will refer to @scheme[my-macro] (more precisely, the name that +referred to @scheme[my-macro] where the macro was used, which may be +different due to renaming, prefixing, etc). + +A macro that expands into a helper macro can insert its own name into +syntax errors raised by the helper macro by installing a +@scheme['report-error-as] syntax property on the helper macro +expression. For example: +@SCHEMEBLOCK[ +(define-syntax (public-macro stx) + (syntax-case stx () + [(public-macro stuff) + (syntax-property + (syntax/loc stx (my-macro stuff other-internal-stuff)) + 'report-error-as + (syntax-e #'public-macro))])) +] + +@;{ +@section[Expand] + +@defmodule[stxclass/util/expand] + +TODO +} + +@section{Miscellaneous utilities} + +@defmodule[stxclass/util/misc] + +@defform[(define-pattern-variable id expr)]{ + +Evaluates @scheme[expr] and binds it to @scheme[id] as a pattern +variable, so @scheme[id] can be used in subsequent @scheme[syntax] +patterns. + +} + +@defform[(with-temporaries (temp-id ...) . body)]{ + +Evaluates @scheme[body] with each @scheme[temp-id] bound as a pattern +variable to a freshly generated identifier. + +For example, the following are equivalent: +@SCHEMEBLOCK[ +(with-temporaries (x) #'(lambda (x) x)) +(with-syntax ([(x) (generate-temporaries '(x))]) + #'(lambda (x) x)) +] + +} + +@defproc[(generate-temporary) identifier?]{ + +Generates one fresh identifier. Singular form of +@scheme[generate-temporaries]. + +} + +@defproc[(generate-n-temporaries [n exact-nonnegative-integer?]) + (listof identifier?)]{ + +Generates a list of @scheme[n] fresh identifiers. + +} + +@defform[(with-catching-disappeared-uses body-expr)]{ + +Evaluates the @scheme[body-expr], catching identifiers looked up using +@scheme[syntax-local-value/catch]. Returns two values: the result of +@scheme[body-expr] and the list of caught identifiers. + +} + +@defform[(with-disappeared-uses stx-expr)]{ + +Evaluates the @scheme[stx-expr], catching identifiers looked up using +@scheme[syntax-local-value/catch]. Adds the caught identifiers to the +@scheme['disappeared-uses] syntax property of the resulting syntax +object. + +} + +@defproc[(syntax-local-value/catch [id identifier?] [predicate (-> any/c boolean?)]) + any/c]{ + +Looks up @scheme[id] in the syntactic environment (as +@scheme[syntax-local-value]). If the lookup succeeds and returns a +value satisfying the predicate, the value is returned and @scheme[id] +is recorded (``caught'') as a disappeared use. If the lookup fails or +if the value does not satisfy the predicate, @scheme[#f] is returned +and the identifier is not recorded as a disappeared use. + +} + + +@defproc[(chunk-kw-seq [stx syntax?] + [table + (listof (cons/c keyword? + (listof (-> syntax? any))))] + [context (or/c syntax? false/c) #f]) + (values (listof (cons/c keyword? (cons/c (syntax/c keyword?) list?))) + syntax?)]{ + +Parses a syntax list into keyword-argument ``chunks'' and a syntax +list tail (the remainder of the syntax list). The syntax of the +keyword arguments is specified by @scheme[table], an association list +mapping keywords to lists of checker procedures. The length of the +checker list is the number of ``arguments'' expected to follow the +keyword, and each checker procedure is applied to the corresponding +argument. The result of the checker procedure is entered into the +chunk for that keyword sequence. The same keyword can appear multiple +times in the result list. + +The @scheme[context] is used to report errors. + +} + +@defproc[(chunk-kw-seq/no-dups + [stx syntax?] + [table + (listof (cons/c keyword? + (listof (-> syntax? any))))] + [context (or/c syntax? false/c) #f]) + (values (listof (cons/c keyword? (cons/c (syntax/c keyword?) list?))) + syntax?)]{ + +Like @scheme[chunk-kw-seq] filtered by @scheme[reject-duplicate-chunks]. + +The @scheme[context] is used to report errors. + +} + +@defproc[(reject-duplicate-chunks + [chunks (listof (cons/c keyword? (cons/c (syntax/c keyword?) list?)))]) + void?]{ + +Raises a syntax error if it encounters the same keyword more than once +in the @scheme[chunks] list. + +The @scheme[context] is used to report errors. + +} + + +@section{Structs} + +@defmodule[stxclass/util/struct] + +@defform[(make struct-id v ...)]{ + +Constructs an instance of @scheme[struct-id], which must be defined +as a struct name. If @scheme[struct-id] has a different number of +fields than the number of @scheme[v] values provided, @scheme[make] +raises a compile-time error. + +} diff --git a/collects/stxclass/stxclass.scrbl b/collects/stxclass/stxclass.scrbl deleted file mode 100644 index 6ba7dce562..0000000000 --- a/collects/stxclass/stxclass.scrbl +++ /dev/null @@ -1,774 +0,0 @@ -#lang scribble/doc -@(require scribble/manual - scribble/struct - scribble/decode - (for-label scheme/base - scheme/contract - stxclass - stxclass/util)) - -@(define ellipses @scheme[...]) -@(define (TODO . pre-flow) - (make-splice - (cons (bold "TODO: ") - (decode-content pre-flow)))) - -@title{Parsing Syntax and Syntax Classes} - -@defmodule[stxclass] - -@bold{Warning: This library is still very volatile! Its interface and -behavior are subject to frequent change. I highly recommend that you -avoid creating PLaneT packages that depend on this library.} - -@section{Parsing Syntax} -@declare-exporting[stxclass] - -@defform/subs[(syntax-parse stx-expr maybe-literals clause ...) - ([maybe-literals code:blank - (code:line #:literals (literal-id ...))] - [clause (syntax-pattern pattern-directive ... expr)])]{ - -Evaluates @scheme[stx-expr], which should produce a syntax object, and -matches it against the patterns in order. If some pattern matches, its -pattern variables are bound to the corresponding subterms of the -syntax object and that clause's side conditions and @scheme[expr] is -evaluated. The result is the result of @scheme[expr]. - -If the syntax object fails to match any of the patterns (or all -matches fail the corresponding clauses' side conditions), a syntax -error is raised. The syntax error indicates the first specific subterm -for which no pattern matches. - -@TODO{Allow literal declarations of form @scheme[(_internal-name -_external-name)].} -} - -@defform[(syntax-parser maybe-literals clause ...)]{ - -Like @scheme[syntax-parse], but produces a matching procedure. The -procedure accepts a single argument, which should be a syntax object. - -} - -The grammar of patterns accepted by @scheme[syntax-parse] and -@scheme[syntax-parser] follows: - -@schemegrammar*[#:literals (_ ...*) - [syntax-pattern - pvar-id - pvar-id:syntax-class-id - literal-id - atomic-datum - (syntax-pattern . syntax-pattern) - (syntax-pattern #,ellipses . syntax-pattern) - ((head ...+) ...* . syntax-pattern)] - [pvar-id - _ - id]] - -Here are the variants of @scheme[syntax-pattern]: - -@specsubform[pvar-id]{ - -Matches anything. The pattern variable is bound to the matched -subterm, unless the pattern variable is the wildcard (@scheme[_]), in -which case no binding occurs. - -} -@specsubform[pvar-id:syntax-class-id]{ - -Matches only subterms specified by the @scheme[_syntax-class-id]. The -syntax class's attributes are computed for the subterm and bound to -the pattern variables formed by prefixing @scheme[_pvar-id.] to the -name of the attribute. @scheme[_pvar-id] is typically bound to the -matched subterm, but the syntax class can substitute a transformed -subterm instead. - -@;{(for example, @scheme[expr/c] wraps the matched -subterm in a @scheme[contract] expression).} - -If @scheme[_pvar-id] is @scheme[_], no pattern variables are bound. - -} -@specsubform[literal-id]{ - -An identifier that appears in the literals list is not a pattern -variable; instead, it is a literal that matches any identifier -@scheme[free-identifier=?] to it. - -} -@specsubform[atomic-datum]{ - -The empty list, numbers, strings, booleans, and keywords match as -literals. - -} -@specsubform[(syntax-pattern . syntax-pattern)]{ - -Matches a syntax pair whose head matches the first pattern and whose -tail matches the second. - -} -@;{ -@specsubform[(syntax-splice-pattern . syntax-pattern)]{ - -Matches a syntax object which consists of any sequence of syntax -objects matching the splice pattern followed by a tail matching the -given tail pattern. - -} -} - -@specsubform[(syntax-pattern #,ellipses . syntax-pattern)]{ - -Matches a sequence of the first pattern ending in a tail matching the -second pattern. - -That is, the sequence pattern matches either the second pattern (which -need not be a list) or a pair whose head matches the first pattern and -whose tail recursively matches the whole sequence pattern. - -} -@specsubform/subs[#:literals (...*) - ((head ...+) ...* . syntax-pattern) - ([head - (code:line (syntax-pattern ...+) head-directive ...)] - [head-directive - (code:line #:min min-reps) - (code:line #:max max-reps) - (code:line #:mand) - #| (code:line #:opt) - (code:line #:occurs occurs-pvar-id) - (code:line #:default default-form) - |#])]{ - -Matches a sequence of any combination of the heads ending in a tail -matching the final pattern. The match is subject to constraints -specified on the heads. - -@specsubform[(code:line #:min min-reps)]{ - -Requires at least @scheme[min-reps] occurrences of the preceding head -to match. @scheme[min-reps] must be a literal exact nonnegative -integer. - -} -@specsubform[(code:line #:max max-reps)]{ - -Requires that no more than @scheme[max-reps] occurrences of the -preceeding head to match. @scheme[max-reps] must be a literal exact -nonnegative integer, and it must be greater than or equal to -@scheme[min-reps]. - -} -@specsubform[#:mand]{ - -Requires that the preceding head occur exactly once. Pattern variables -in the preceding head are not bound at a higher ellipsis nesting -depth. - -} -@;{ -@specsubform[#:opt]{ - -(Probably a bad idea.) - -} -} -} -@;{ -The variants of @scheme[_syntax-splice-pattern] follow: - -@specsubform[pvar-id:syntax-splice-class-id]{ - -Matches a sequence of syntax objects described by -@scheme[_syntax-splice-class-id]. - -The name @scheme[_pvar-id] is bound, but not allowed within -expressions or @scheme[syntax] templates (since it does not refer to a -particular syntax object). Only the prefixed attributes of the splice -class are usable. -} -} - -Both @scheme[syntax-parse] and @scheme[syntax-parser] support -directives for annotating the pattern and specifying side -conditions. The grammar for pattern directives follows: - -@schemegrammar[pattern-directive - (code:line #:declare pattern-id syntax-class-id) - (code:line #:declare pattern-id (syntax-class-id expr ...)) - (code:line #:with syntax-pattern expr) - (code:line #:when expr)] - -@specsubform[(code:line #:declare pvar-id syntax-class-id)] -@specsubform[(code:line #:declare pvar-id (syntax-class-id expr ...))]{ - -The first form is equivalent to using the -@scheme[_pvar-id:syntax-class-id] form in the pattern (but it is -illegal to use both for a single pattern variable). The -@scheme[#:declare] form may be preferred when writing macro-defining -macros or to avoid dealing with structured identifiers. - -The second form allows the use of parameterized syntax classes, which -cannot be expressed using the ``colon'' notation. The @scheme[expr]s -are evaluated outside the scope of the pattern variable bindings. - -} -@specsubform[(code:line #:with syntax-pattern expr)]{ - -Evaluates the @scheme[expr] in the context of all previous pattern -variable bindings and matches it against the pattern. If the match -succeeds, the new pattern variables are added to the environment for -the evaluation of subsequent side conditions. If the @scheme[#:with] -match fails, the matching process backtracks. Since a syntax object -may match a pattern in several ways, backtracking may cause the same -clause to be tried multiple times before the next clause is reached. - -} -@specsubform[(code:line #:when expr)]{ - -Evaluates the @scheme[expr] in the context of all previous pattern -variable bindings. If it produces a false value, the matching process -backtracks as described above; otherwise, it continues. - -} - -@defidform[...*]{ - -Keyword recognized by @scheme[syntax-parse] etc as notation for -generalized sequences. It may not be used as an expression. - -} - -@section{Syntax Classes} -@declare-exporting[stxclass] - -Syntax classes provide an abstraction mechanism for the specification -of syntax. Basic syntax classes include @scheme[identifier] and -@scheme[keyword]. From these, programmers can build larger -specifications of syntax, such as @scheme[distinct-identifier-list] -and @scheme[keyword-formals]. Macros that manipulate the same -syntactic structures can share syntax class definitions. Syntax -classes are also the basis of error reporting in -@scheme[syntax-parse]. - -When a syntax class accepts (matches, includes) a syntax object, it -computes and provides attributes based on the contents of the matched -syntax. While the values of the attributes depend on the matched -syntax, the set of attributes and each attribute's ellipsis nesting -depth is fixed for each syntax class. - -@defform*/subs[#:literals (pattern) - [(define-syntax-class name-id stxclass-option ... - stxclass-variant ...+) - (define-syntax-class (name-id arg-id ...) stxclass-option ... - stxclass-variant ...+)] - ([stxclass-options - (code:line #:attributes (attr-arity-decl ...)) - (code:line #:description description) - (code:line #:transparent)] - [attr-arity-decl - attr-name-id - (attr-name-id depth)] - [stxclass-variant - (pattern syntax-pattern pattern-directive ...)])]{ - -Defines @scheme[name-id] as a syntax class. When the @scheme[arg-id]s -are present, they are bound as variables (not pattern variables) in -the body. - -The body of the syntax-class definition contains one or more variants -that specify the syntax it accepts and determines the attributes it -provides. The syntax class provides only those attributes which are -present in every variant. Each such attribute must be defined with the -same ellipsis nesting depth and the same sub-attributes in each -component. - -@specsubform[(code:line #:attributes (attr-arity-decl ...))]{ - -Declares the attributes of the syntax class. An attribute arity -declaration consists of the attribute name and optionally its ellipsis -depth (zero if not explicitly specified). - -If the attributes are not explicitly listed, they are -inferred. Attribute inference does not take into account attributes -from the current syntax class and from syntax classes that have not -yet been defined. The full set of attributes is available, however, to -@scheme[#:with] and @scheme[#:when] expressions. This treatment of -recursive and forward references prevents infinitely nested -attributes. - -} - -@specsubform[(code:line #:description description)]{ - -The @scheme[description] argument is an expression (with the -syntax-class's parameters in scope) that should evaluate to a -string. It is used in error messages involving the syntax class. For -example, if a term is rejected by the syntax class, an error of the -form @scheme["expected "] may be generated. - -If absent, the name of the syntax class is used instead. - -} - -@specsubform[#:transparent]{ - -Indicates that errors may be reported with respect to the internal -structure of the syntax class. -} - -@specsubform/subs[#:literals (pattern) - (pattern syntax-pattern stxclass-pattern-directive ...) - ([stxclass-pattern-directive - pattern-directive - (code:line #:rename internal-id external-id)])]{ - -Accepts syntax matching the given pattern with the accompanying -pattern directives as in @scheme[syntax-parse]. - -The attributes of the pattern are the pattern variables within the -@scheme[pattern] form together with all pattern variables bound by -@scheme[#:with] clauses, including nested attributes produced by -syntax classes associated with the pattern variables. - -The name of an attribute is the symbolic name of the pattern variable, -except when the name is explicitly given via a @scheme[#:rename] -clause. - -@specsubform[(code:line #:rename internal-id external-id)]{ - -Exports the pattern variable binding named by @scheme[internal-id] as -the attribute named @scheme[external-id]. - -} -} - -} - -@defidform[pattern]{ - -Keyword recognized by @scheme[define-syntax-class]. It may not be -used as an expression. -} - -@defform[(define-basic-syntax-class (name-id arg-id ...) - ([attr-id attr-depth] ...) - parser-expr)]{ - -Defines @scheme[name] as a syntax class with the given -attributes. Each @scheme[attr-depth] must be a literal exact -nonnegative integer (most often @scheme[0]). - -The @scheme[arg-id]s are placeholders only. They are not bound in any -part of the syntax-class body. - -The @scheme[parser-expr] must evaluate to a procedure. This procedure -is used to parse or reject syntax objects. The arguments to the parser -procedure consist of the syntax object to parse followed by the -syntax-class parameterization arguments. To indicate success, the -parser should return a list of attribute values, one for each -attribute listed. (For example, a parser for a syntax class that -defines no attributes returns the empty list when it succeeds.) To -indicate failure, the parser procedure should return @scheme[#f]. - -The parser procedure is encouraged to avoid side-effects. - -@TODO{Add support for better error reporting within basic syntax -class.} - -@TODO{Remove this form in favor of new stxclass-variant form.} - -} -@;{ -@defform[(define-basic-syntax-class* (name-id arg-id ...) - ([attr-id attr-depth] ...) - parser-expr)]{ - -Like @scheme[define-basic-syntax-class], except that on success the -parser expression produces a list with an extra element consed onto -the attribute value list. This extra element is the transformed -matched syntax. When a pattern variable annotated with the syntax -class matches, it is bound to this syntax instead of the subterm of -the original syntax object. - -Transforming matched syntax is useful for implementing syntax classes -that add interpretation or constraints to expressions, such as -@scheme[expr/c], which imposes run-time contracts on expressions. - -} -} - -@;{ -@defform[(attrs-of syntax-class-id)]{ - -For debugging. Returns a representation of the attributes provided by -a syntax class. - -} - -@defform[(parse-sc syntax-class-id stx-expr arg-expr ...)]{ - -For debugging. Runs the parser for the syntax class (parameterized by -the @scheme[arg-expr]s) on the syntax object produced by -@scheme[stx-expr]. - -} -} - - -@section{Library syntax classes} -@declare-exporting[stxclass] - -@(define-syntax-rule (defstxclass name . pre-flows) - (defidform name . pre-flows)) - -@(define-syntax-rule (defstxclass* (name arg ...) . pre-flows) - (defform (name arg ...) . pre-flows)) - -@defstxclass[expr]{ - -Matches anything except a keyword literal (to distinguish expressions -from the start of a keyword argument sequence). Does not expand or -otherwise inspect the term. - -} - -@deftogether[( -@defstxclass[identifier] -@defstxclass[boolean] -@defstxclass[str] -@defstxclass[char] -@defstxclass[keyword] -@defstxclass[number] -@defstxclass[integer] -@defstxclass[exact-integer] -@defstxclass[exact-nonnegative-integer] -@defstxclass[exact-positive-integer])]{ - -Match syntax satisfying the corresponding predicates. - -} - -Two of the above have short aliases: - -@defstxclass[id]{ Same as @scheme[identifier]. } -@defstxclass[nat]{ Same as @scheme[exact-nonnegative-integer]. } - -The following syntax classes mirror parts of the macro API. They may -only be used during transformation (when @scheme[syntax-transforming?] -returns true). Otherwise they may raise an error. - -@defstxclass[static]{ - -Matches identifiers that are bound in the syntactic environment to -static information (see @scheme[syntax-local-value]). Attribute -@scheme[_value] contains the value the name is bound to. - -} - -@defform[(static-of description predicate)]{ - -Refines @scheme[static]: matches identifiers that are bound in the -syntactic environment to static information satisfying the given -@scheme[predicate]. Attribute @scheme[_value] contains the value the -name is bound to. The @scheme[description] argument is used for error -reporting. - -} - -@;{ -@defstxclass[struct-name]{ - -Matches identifiers bound to static struct information. Attributes are -@scheme[_descriptor], @scheme[_constructor], @scheme[_predicate], -@scheme[(_accessor ...)], @scheme[_super], and @scheme[_complete?]. - -} -} -@;{ -@defstxclass[expr/local-expand]{ - -Matches any term and @scheme[local-expand]s it as an expression with -an empty stop list. Attribute @scheme[_expanded] is the expanded form. - -} - -@defstxclass[expr/head-local-expand] -@defstxclass[block/head-local-expand] -@defstxclass[internal-definitions] -} - -@;{ -@defform[(expr/c contract-expr-stx)]{ - -Accepts any term and returns as the match that term wrapped in a -@scheme[contract] expression enforcing @scheme[contract-expr-stx]. - -} -} - - -@;{ - - -1 How to abstract over similar patterns: - -(syntax-parse stx #:literals (blah bleh blaz kwA kwX) - [(blah (bleh (kwX y z)) blaz) - ___] - [(blah (bleh (kwA (b c))) blaz) - ___]) - -=> - -(define-syntax-class common - #:attributes (inner) - #:literals (blah bleh blaz) - (pattern (blah (bleh inner) blaz))) -(syntax-parse stx #:literals (kwA kwX) - [c:common - #:with (kwX y z) #'c.inner - ___] - [c:common - #:with (kwA (b c)) #'c.inner - ___]) - - -OR => - -(define-syntax-class (common expected-kw) - #:attributes (inner) - #:literals (blah bleh blaz) - (pattern (blah (bleh (kw . inner)) blaz) - #:when (free-identifier=? #'kw expected-kw))) -(syntax-parse stx - [c - #:declare c (common #'kwX) - #:with (y z) #'c.inner - ___] - [c - #:declare c (common #'kwA) - #:with ((b c)) #'c.inner - ___]) - - -} - - -@section{Utilities} - -The @schememodname[stxclass] collection includes several utility -modules. They are documented individually below. - -As a shortcut, the @schememodname[stxclass/util] module provides all -of the contents of the separate utility modules: - -@defmodule[stxclass/util] - -@subsection{Error reporting} - -@defmodule[stxclass/util/error] - -The @schememodname[scheme/base] and @schememodname[scheme] languages -provide the @scheme[raise-syntax-error] procedure for reporting syntax -errors. Using @scheme[raise-syntax-error] effectively requires passing -around either a symbol indicating the special form that signals the -error or else a ``contextual'' syntax object from which the special -form's name can be extracted. This library helps manage the contextual -syntax for reporting errors. - -@defparam[current-syntax-context stx (or/c syntax? false/c)]{ - -The current contextual syntax object, defaulting to @scheme[#f]. It -determines the special form name that prefixes syntax errors created -by @scheme[wrong-syntax], as follows: - -If it is a syntax object with a @scheme['report-error-as] syntax -property whose value is a symbol, then that symbol is used as the -special form name. Otherwise, the same rules apply as in -@scheme[raise-syntax-error]. - -} - -@defproc[(wrong-syntax [stx syntax?] [format-string string?] [v any/c] ...) - any]{ - -Raises a syntax error using the result of -@scheme[(current-syntax-context)] as the ``major'' syntax object and -the provided @scheme[stx] as the specific syntax object. (The latter, -@scheme[stx], is usually the one highlighted by DrScheme.) The error -message is constructed using the format string and arguments, and it -is prefixed with the special form name as described under -@scheme[current-syntax-context]. - -} - -A macro using this system might set the syntax context at the very -beginning of its transformation as follows: -@SCHEMEBLOCK[ -(define-syntax (my-macro stx) - (parameterize ((current-syntax-context stx)) - (syntax-case stx () - ___))) -] -Then any calls to @scheme[wrong-syntax] during the macro's -transformation will refer to @scheme[my-macro] (more precisely, the name that -referred to @scheme[my-macro] where the macro was used, which may be -different due to renaming, prefixing, etc). - -A macro that expands into a helper macro can insert its own name into -syntax errors raised by the helper macro by installing a -@scheme['report-error-as] syntax property on the helper macro -expression. For example: -@SCHEMEBLOCK[ -(define-syntax (public-macro stx) - (syntax-case stx () - [(public-macro stuff) - (syntax-property - (syntax/loc stx (my-macro stuff other-internal-stuff)) - 'report-error-as - (syntax-e #'public-macro))])) -] - -@;{ -@subsection[Expand] - -@defmodule[stxclass/util/expand] - -TODO -} - -@subsection{Miscellaneous utilities} - -@defmodule[stxclass/util/misc] - -@defform[(define-pattern-variable id expr)]{ - -Evaluates @scheme[expr] and binds it to @scheme[id] as a pattern -variable, so @scheme[id] can be used in subsequent @scheme[syntax] -patterns. - -} - -@defform[(with-temporaries (temp-id ...) . body)]{ - -Evaluates @scheme[body] with each @scheme[temp-id] bound as a pattern -variable to a freshly generated identifier. - -For example, the following are equivalent: -@SCHEMEBLOCK[ -(with-temporaries (x) #'(lambda (x) x)) -(with-syntax ([(x) (generate-temporaries '(x))]) - #'(lambda (x) x)) -] - -} - -@defproc[(generate-temporary) identifier?]{ - -Generates one fresh identifier. Singular form of -@scheme[generate-temporaries]. - -} - -@defproc[(generate-n-temporaries [n exact-nonnegative-integer?]) - (listof identifier?)]{ - -Generates a list of @scheme[n] fresh identifiers. - -} - -@defform[(with-catching-disappeared-uses body-expr)]{ - -Evaluates the @scheme[body-expr], catching identifiers looked up using -@scheme[syntax-local-value/catch]. Returns two values: the result of -@scheme[body-expr] and the list of caught identifiers. - -} - -@defform[(with-disappeared-uses stx-expr)]{ - -Evaluates the @scheme[stx-expr], catching identifiers looked up using -@scheme[syntax-local-value/catch]. Adds the caught identifiers to the -@scheme['disappeared-uses] syntax property of the resulting syntax -object. - -} - -@defproc[(syntax-local-value/catch [id identifier?] [predicate (-> any/c boolean?)]) - any/c]{ - -Looks up @scheme[id] in the syntactic environment (as -@scheme[syntax-local-value]). If the lookup succeeds and returns a -value satisfying the predicate, the value is returned and @scheme[id] -is recorded (``caught'') as a disappeared use. If the lookup fails or -if the value does not satisfy the predicate, @scheme[#f] is returned -and the identifier is not recorded as a disappeared use. - -} - - -@defproc[(chunk-kw-seq [stx syntax?] - [table - (listof (cons/c keyword? - (listof (-> syntax? any))))] - [context (or/c syntax? false/c) #f]) - (values (listof (cons/c keyword? (cons/c (syntax/c keyword?) list?))) - syntax?)]{ - -Parses a syntax list into keyword-argument ``chunks'' and a syntax -list tail (the remainder of the syntax list). The syntax of the -keyword arguments is specified by @scheme[table], an association list -mapping keywords to lists of checker procedures. The length of the -checker list is the number of ``arguments'' expected to follow the -keyword, and each checker procedure is applied to the corresponding -argument. The result of the checker procedure is entered into the -chunk for that keyword sequence. The same keyword can appear multiple -times in the result list. - -The @scheme[context] is used to report errors. - -} - -@defproc[(chunk-kw-seq/no-dups - [stx syntax?] - [table - (listof (cons/c keyword? - (listof (-> syntax? any))))] - [context (or/c syntax? false/c) #f]) - (values (listof (cons/c keyword? (cons/c (syntax/c keyword?) list?))) - syntax?)]{ - -Like @scheme[chunk-kw-seq] filtered by @scheme[reject-duplicate-chunks]. - -The @scheme[context] is used to report errors. - -} - -@defproc[(reject-duplicate-chunks - [chunks (listof (cons/c keyword? (cons/c (syntax/c keyword?) list?)))]) - void?]{ - -Raises a syntax error if it encounters the same keyword more than once -in the @scheme[chunks] list. - -The @scheme[context] is used to report errors. - -} - - -@subsection{Structs} - -@defmodule[stxclass/util/struct] - -@defform[(make struct-id v ...)]{ - -Constructs an instance of @scheme[struct-id], which must be defined -as a struct name. If @scheme[struct-id] has a different number of -fields than the number of @scheme[v] values provided, @scheme[make] -raises a compile-time error. - -} diff --git a/collects/tests/stxclass/stxclass.ss b/collects/tests/stxclass/stxclass.ss index 96e8c33f40..c657c79300 100644 --- a/collects/tests/stxclass/stxclass.ss +++ b/collects/tests/stxclass/stxclass.ss @@ -270,3 +270,29 @@ (pattern f:frob)) (define-syntax-class frob (pattern x:id)) + +(syntax-parse #'1 + [x:nat + (define (check d) + (unless (positive? d) + (error "not positive"))) + (check #'x.datum) + 'ok]) + + +(define-syntax-class Opaque + (pattern (a:id n:nat))) +(define-syntax-class Transparent + #:transparent + (pattern (a:id n:nat))) + +(with-handlers ([exn? exn-message]) + (syntax-parse #'(0 1) [_:Opaque 'ok])) +(with-handlers ([exn? exn-message]) + (syntax-parse #'(0 1) [_:Transparent 'ok])) + +#; +(syntax-parse #'1 + [_:Transparent 'ok] + [(a b) 'ok]) +