On this page:
6.1 Raising exn:  fail:  read
raise-read-error
raise-read-eof-error
6.2 Module Reader
#%module-begin
make-meta-reader
wrap-read-all

6 Reader Helpers

6.1 Raising exn:fail:read

 (require syntax/readerr)

procedure

(raise-read-error msg-string    
  source    
  line    
  col    
  pos    
  span    
  [#:extra-srclocs extra-srclocs])  any
  msg-string : string?
  source : any/c
  line : (or/c number? false/c)
  col : (or/c number? false/c)
  pos : (or/c number? false/c)
  span : (or/c number? false/c)
  extra-srclocs : (listof srcloc?) = '()
Creates and raises an exn:fail:read exception, using msg-string as the base error message.

Source-location information is added to the error message using the last five arguments and the extra-srclocs (if the error-print-source-location parameter is set to #t). The source argument is an arbitrary value naming the source location—usually a file path string. Each of the line, pos arguments is #f or a positive exact integer representing the location within source-name (as much as known), col is a non-negative exact integer for the source column (if known), and span is #f or a non-negative exact integer for an item range starting from the indicated position.

The usual location values should point at the beginning of whatever it is you were reading, and the span usually goes to the point the error was discovered.

procedure

(raise-read-eof-error msg-string    
  source    
  line    
  col    
  pos    
  span)  any
  msg-string : string?
  source : any/c
  line : (or/c number? false/c)
  col : (or/c number? false/c)
  pos : (or/c number? false/c)
  span : (or/c number? false/c)
Like raise-read-error, but raises exn:fail:read:eof instead of exn:fail:read.

6.2 Module Reader

See also Defining new #lang Languages in The Racket Guide.

 (require syntax/module-reader)

The syntax/module-reader library provides support for defining #lang readers. It is normally used as a module language, though it may also be required to get make-meta-reader. It provides all of the bindings of racket/base other than #%module-begin.

syntax

(#%module-begin module-path)

(#%module-begin module-path reader-option ... form ....)
(#%module-begin             reader-option ... form ....)
 
reader-option = #:read        read-expr
  | #:read-syntax read-syntax-expr
  | #:whole-body-readers? whole?-expr
  | #:wrapper1    wrapper1-expr
  | #:wrapper2    wrapper2-expr
  | #:language    lang-expr
  | #:info        info-expr
  | #:language-info language-info-expr
 
  read-expr : (input-port? . -> . any/c)
  read-syntax-expr : (any/c input-port? . -> . any/c)
  whole-expr : any/c
  wrapper1-expr : 
(or/c ((-> any/c) . -> . any/c)
      ((-> any/c) boolean? . -> . any/c))
  wrapper2-expr : 
(or/c (input-port? (input-port? . -> . any/c)
       . -> . any/c)
      (input-port? (input-port? . -> . any/c)
       boolean? . -> . any/c))
  info-expr : (symbol? any/c (symbol? any/c . -> . any/c) . -> . any/c)
  module-info-expr : (or/c (vector/c module-path? symbol? any/c) #f)
  lang-expr : 
(or/c module-path?
      (and/c syntax? (compose module-path? syntax->datum))
      procedure?)
In its simplest form, the body of a module written with syntax/module-reader contains just a module path, which is used in the language position of read modules. For example, a module something/lang/reader implemented as

(module reader syntax/module-reader
  module-path)

creates a reader such that a module source

#lang something
....

is read as

(module name-id module-path
  (#%module-begin ....))

where name-id is derived from the source input port’s name in the same way as for #lang s-exp.

Keyword-based reader-options allow further customization, as listed below. Additional forms are as in the body of racket/base module; they can import bindings and define identifiers used by the reader-options.

As another example, the following reader defines a “language” that ignores the contents of the file, and simply reads files as if they were empty:

(module ignored syntax/module-reader
  racket/base
  #:wrapper1 (lambda (t) (t) '()))

Note that the wrapper still performs the read, otherwise the module loader would complain about extra expressions.

As a more useful example, the following module language is similar to at-exp, where the first datum in the file determines the actual language (which means that the library specification is effectively ignored):

(module reader syntax/module-reader
  -ignored-
  #:wrapper2
  (lambda (in rd stx?)
    (let* ([lang (read in)]
           [mod  (parameterize ([current-readtable
                                 (make-at-readtable)])
                   (rd in))]
           [mod  (if stx? mod (datum->syntax #f mod))]
           [r (syntax-case mod ()
                [(module name lang* . body)
                 (with-syntax ([lang (datum->syntax
                                      #'lang* lang #'lang*)])
                   (syntax/loc mod (module name lang . body)))])])
      (if stx? r (syntax->datum r))))
  (require scribble/reader))

The ability to change the language position in the resulting module expression can be useful in cases such as the above, where the base language module is chosen based on the input. To make this more convenient, you can omit the module-path and instead specify it via a #:language expression. This expression can evaluate to a datum or syntax object that is used as a language, or it can evaluate to a thunk. In the latter case, the thunk is invoked to obtain such a datum before reading the module body begins, in a dynamic extent where current-input-port is the source input. A syntax object is converted using syntax->datum when a datum is needed (for read instead of read-syntax). Using #:language, the last example above can be written more concisely:

(module reader syntax/module-reader
  #:language read
  #:wrapper2 (lambda (in rd stx?)
               (parameterize ([current-readtable
                               (make-at-readtable)])
                 (rd in)))
  (require scribble/reader))

For such cases, however, the alternative reader constructor make-meta-reader implements a might tightly controlled reading of the module language.

procedure

(make-meta-reader self-sym 
  path-desc-str 
  [#:read-spec read-spec] 
  module-path-parser 
  convert-read 
  convert-read-syntax 
  convert-get-info) 
  
procedure? procedure? procedure?
  self-sym : symbol?
  path-desc-str : string?
  read-spec : (input-port? . -> . any/c) = (lambda (in) ....)
  module-path-parser : 
(any/c . -> . (or/c module-path? #f
                    (vectorof module-path?)))
  convert-read : (procedure? . -> . procedure?)
  convert-read-syntax : (procedure? . -> . procedure?)
  convert-get-info : (procedure? . -> . procedure?)
Generates procedures suitable for export as read (see read and #lang), read-syntax (see read-syntax and #lang), and get-info (see read-language and #lang), respectively, where the procedures chains to another language that is specified in an input stream.

The at-exp, reader, and planet languages are implemented using this function.

The generated functions expect a target language description in the input stream that is provided to read-spec. The default read-spec extracts a non-empty sequence of bytes after one or more space and tab bytes, stopping at the first whitespace byte or end-of-file (whichever is first), and it produces either such a byte string or #f. If read-spec produces #f, a reader exception is raised, and path-desc-str is used as a description of the expected language form in the error message.

The reader language supplies read for read-spec. The at-exp and planet languages use the default read-spec.

The result of read-spec is converted to a module path using module-path-parser. If module-path-parser produces a vector of module paths, they are tried in order using module-declared?. If module-path-parser produces #f, a reader exception is raised in the same way as when read-spec produces a #f. The planet languages supply a module-path-parser that converts a byte string to a module path.

If loading the module produced by module-path-parser succeeds, then the loaded module’s read, read-syntax, or get-info export is passed to convert-read, convert-read-syntax, or convert-get-info, respectively.

The at-exp language supplies convert-read and convert-read-syntax to add @-expression support to the current readtable before chaining to the given procedures.

The procedures generated by make-meta-reader are not meant for use with the syntax/module-reader language; they are meant to be exported directly.

procedure

(wrap-read-all mod-path    
  in    
  read    
  mod-path-stx    
  src    
  line    
  col    
  pos)  any/c
  mod-path : module-path?
  in : input-port?
  read : (input-port . -> . any/c)
  mod-path-stx : syntax?
  src : (or/c syntax? #f)
  line : number?
  col : number?
  pos : number?
This function is deprecated; the syntax/module-reader language can be adapted using the various keywords to arbitrary readers; please use it instead.

Repeatedly calls read on in until an end of file, collecting the results in order into lst, and derives a name-id from (object-name in) in the same way as #lang s-exp. The last five arguments are used to construct the syntax object for the language position of the module. The result is roughly

`(module ,name-id ,mod-path ,@lst)