www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

debug-scopes.scrbl (8104B)


      1 #lang scribble/manual
      2 @require[scribble/example
      3          scribble-enhanced/doc
      4          @for-label[debug-scopes
      5                     racket/base
      6                     racket/contract]]
      7 
      8 @title{Debuging scope-related issues}
      9 @author[@author+email["Suzanne Soy" "racket@suzanne.soy"]]
     10 
     11 @defmodule[debug-scopes]
     12 
     13 @defproc[(+scopes [stx syntax?]) string?]{The identifiers are adorned with
     14  superscripts indicating the scopes present on them. Each scope is represented
     15  as an ascending integer which is unique within the current expansion. At the
     16  end of the expansion, a table showing the equivalence between the small
     17  integers and the scopes as represented by Racket is printed. Ranges of
     18  consecutive scopes are represented as @racket[identifier³˙˙⁹] (which would
     19  indicate that the scopes 3, 4, 5, 6, 7, 8 and 9 are present on the
     20  identifier). When only a few scopes are missing from the range, they are
     21  printed as @racket[identifier³˙˙⁹⁻⁵⁻⁷] (which would indicate that the scopes
     22  3, 4, 6, 8 and 9 are present on the identifier). When there are too many
     23  missing identifiers within the range, the scopes are instead displayed
     24  alternatively as superscripts and subscripts, e.g.
     25  @racket[identifier²₃⁵₇¹¹₁₃¹⁷₁₉] (which would indicate that only the scopes 2,
     26  3, 5, 7, 11, 17 and 19 are present on the identifier, and would also indicate
     27  that a developer is playing a trick on you). Finally the current macro scope
     28  (which can be removed using @racket[syntax-local-value]) and the current
     29  use-site scope, if any (which can be removed using
     30  @racket[syntax-local-identifier-as-binding]) is printed for the whole
     31  expression, using the notation @racket[(expression …)ˢˡⁱ⁼⁴⁺ᵘˢᵉ⁼¹²] (which
     32  would indicate that the macro scope is 4 and the use-site scope is 12).
     33 
     34  @examples[#:lang racket
     35  (require (for-syntax racket/base
     36                       debug-scopes))
     37  (define-syntax (foo stx)
     38    (displayln (+scopes stx))
     39    (displayln (+scopes (datum->syntax #f 'no-scopes)))
     40    (displayln (+scopes (syntax-local-introduce #'here)))
     41    (print-full-scopes)
     42    #'(void))
     43 
     44  (foo (list 123))]
     45 
     46  When using named scopes---for example, via
     47  @racketmodname[debug-scopes/named-scopes/override]---a named scope is often
     48  used instead of the macro scope flipped by @racket[syntax-local-introduce]. If
     49  @racket[+scopes] is called within that context, it also annotates the whole
     50  expression with the named scope which acts as a replacement for the macro
     51  scope, using the notation @racket[(expression …)ˢˡⁱ⁼⁴⁺ᵘˢᵉ⁼¹²⁽ⁿᵃᵐᵉᵈ⁼⁵⁾] (which
     52  would indicate that the original macro scope was 4, the use-site-scope is 12,
     53  and the named macro scope is 5).
     54 
     55 @examples[#:lang racket
     56  (require (for-syntax (except-in racket/base syntax-local-introduce)
     57                       debug-scopes
     58                       debug-scopes/named-scopes))
     59  (define-syntax (foo stx)
     60    (displayln (+scopes stx))
     61    (displayln (+scopes (datum->syntax #f 'no-scopes)))
     62    (displayln (+scopes (syntax-local-introduce #'here)))
     63    (print-full-scopes)
     64    #'(void))
     65 
     66  (foo (list 123))]}
     67 
     68 You can combine @racket[+scopes] with @racketmodname[racket/trace] to trace
     69 scopes through expansion.
     70 @examples[
     71   (require racket/trace (for-syntax racket/base racket/trace debug-scopes))
     72   (begin-for-syntax
     73     (define (maybe-syntax->scoped syn?)
     74       (if (syntax? syn?)
     75           (+scopes syn?)
     76           syn?))
     77     (current-trace-print-args
     78       (let ([ctpa (current-trace-print-args)])
     79         (lambda (s l kw l2 n)
     80           (ctpa s (map maybe-syntax->scoped l) kw l2 n))))
     81     (current-trace-print-results
     82       (let ([ctpr (current-trace-print-results)])
     83         (lambda (s l n)
     84          (ctpr s (map maybe-syntax->scoped l) n)))))
     85   (trace-define-syntax let
     86     (syntax-rules ()
     87       [(_ ([x v]) e) ((lambda (x) e) v)]))
     88   (let ([x 5]) (let ([y 120]) y))]
     89 
     90 @defproc[(print-full-scopes [reset? any/c #t]) void?]{ Prints the long scope id
     91  and annotation for all scopes displayed as part of preceeding calls to
     92  @racket[+scopes], as would be shown by
     93  @racket[(hash-ref (syntax-debug-info stx) 'context)].
     94 
     95  This allows to get some extended information about the scopes in a summary
     96  table by calling @racket[print-full-scopes], while still getting short and
     97  readable display of syntax objects with @racket[+scopes].
     98 
     99  After running @racket[(print-full-scopes)], if @racket[reset?] is true, then
    100  the scope counter is reset (and @racket[+scopes] therefore starts numbering
    101  scopes starting from @racket[0] again).}
    102 
    103 @section{Hack for named scopes}
    104 
    105 @defmodule[debug-scopes/named-scopes/exptime]
    106 
    107 Module scopes bear are annotated by Racket with the name of the module. As of
    108 December 2016, other scopes like macro scopes@note{Both the ones implicitly
    109  created when a macro is called, and the ones explicitly created via
    110  @racket[make-syntax-introducer] are concerned by this} or use-site scopes lack
    111 any form of annotation or naming.
    112 
    113 @defproc[(make-named-scope [name (or/c string? symbol?)])
    114          (->* (syntax?) ([or/c 'add 'remove 'flip]) syntax?)]{ This function
    115  uses a hack to create named scopes on demand: it creates a dummy mododule with
    116  the desired name, expands it and extracts the module's scope. The exact
    117  implementation mechanism may vary in future versions, for example if later
    118  versions of Racket directly support the creation of named scopes,
    119  @racket[make-named-scope] would simply become an alias for the official
    120  mechanism. Later versions of this function may therefore produce named scopes
    121  other than module-like scopes.}
    122 
    123 @defproc[(make-module-like-named-scope [name (or/c string? symbol?)])
    124          (->* (syntax?) ([or/c 'add 'remove 'flip]) syntax?)]{
    125  Produces a named module-like scope. The @racket[make-named-scope] function
    126  currently also produces a module-like scope, so the two are equivalent for now.
    127  In later versions, @racket[make-named-scope] may produce other sorts of named
    128  scopes if they can be created more efficiently, but
    129  @racket[make-module-like-named-scope] will always produce module-like scopes.}
    130 
    131 @define[orig:define-syntax @racket[define-syntax]]
    132 @define[orig:syntax-local-introduce @racket[syntax-local-introduce]]
    133 
    134 @subsection{Automatic use of named scopes}
    135 
    136 @defmodule[debug-scopes/named-scopes/override]
    137 
    138 This module overrides @orig:define-syntax and @orig:syntax-local-introduce to
    139 automatically use a named macro scope. The use-site scope is not affected for
    140 now, as the original unnamed use-site scope from Racket benefits from special
    141 cooperation from definition contexts, which would be hard to achieve with the
    142 hack currently used to implement named scopes.
    143 
    144 @defform*[((define-syntax (name stx-arg) . body)
    145            (define-syntax name value))]{
    146 
    147  Like @orig:define-syntax, but the first form changes the macro scope
    148  introduced by @racket[syntax-local-introduce] to use a named scope, bearing
    149  the @racket[name] of the macro.
    150 
    151  Note that this change only affects the scopes introduced by the overriden
    152  version of @racket[syntax-local-introduce], not the original
    153  @|orig:syntax-local-introduce|.
    154 
    155  This means that if the macro calls a function defined in another file which
    156  uses the non-overidden version of @orig:syntax-local-introduce, both the
    157  original unnamed scope and the named scope may accidentally appear in the
    158  result. Macros defined using the overridden @racket[syntax-local-introduce]
    159  should therefore take special care to always use the overridden version of
    160  @racket[syntax-local-introduce].
    161 
    162  The use-site scope is not affected for now, as the original unnamed use-site
    163  scope from Racket benefits from special cooperation from definition contexts,
    164  which would be hard to achieve with the hack currently used to implement named
    165  scopes.}
    166 
    167 @defproc[(syntax-local-introduce [stx syntax?]) syntax?]{ Like
    168  @orig:syntax-local-introduce, but uses the named scope set up by
    169  @racket[define-syntax] if called within the dynamic extent of a call to a
    170  macro defined by the overridden @racket[define-syntax] (and otherwise behaves
    171  like the original @orig:syntax-local-introduce).}