▼ |  | Syntax: Meta-Programming Helpers

1 | Syntax Object Helpers
2 | Module-Processing Helpers
3 | Macro Transformer Helpers
4 | Reader Helpers
5 | Non-Module Compilation And Expansion
6 | Trusting Standard Recertifying Transformers
7 | Attaching Documentation to Exports
8 | Parsing and classifying syntax
| Index

► | 4 | Reader Helpers

4.1 | Raising exn: fail: read
4.2 | Module Reader

On this page:

4.1`` Raising exn: fail: read
raise-read-error
raise-read-eof-error
4.2`` Module Reader
#%module-begin
make-meta-reader
wrap-read-all

top ← prev up next →

### 4`` Reader Helpers

#### 4.1`` Raising exn:fail:read

( require syntax/readerr )

( raise-read-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 )

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 (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.

( 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.

#### 4.2`` Module Reader

( require syntax/module-reader )

The syntax/module-reader language provides support for defining #lang readers. In its simplest form, the only thing that is needed in the body of a syntax/module-reader is the name of the module that will be used in the language position of read modules; using keywords, the resulting readers can be customized in a number of ways.

( #%module-begin module-path)
( #%module-begin module-path reader-option ... body ....)
( #%module-begin reader-option ... body ....)

reader-option |  | = |  | #:language lang-expr
|  | | |  | #:read read-expr
|  | | |  | #:read-syntax read-syntax-expr
|  | | |  | #:info info-expr
|  | | |  | #:wrapper1 wrapper1-expr
|  | | |  | #:wrapper2 wrapper2-expr
|  | | |  | #:whole-body-readers? whole?-expr

Causes a module written in the syntax/module-reader language to define and provide read and read-syntax functions, making the module an implementation of a reader. In particular, the exported reader functions read all S-expressions until an end-of-file, and package them into a new module in the module-path language.

That is, a module something/lang/reader implemented as

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

creates a reader that converts #lang something into

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

where name-id is derived from the name of the port used by the reader, or anonymous-module if the port has no name.

For example, scheme/base/lang/reader is implemented as

( module reader syntax/module-reader
scheme/base)

The reader functions can be customized in a number of ways, using keyword markers in the syntax of the reader module. A #:read and #:read-syntax keywords can be used to specify functions other than read and read-syntax to perform the reading. For example, you can implement a Honu reader using:

( module reader syntax/module-reader
honu
#:read read-honu
#:read-syntax read-honu-syntax )

Similarly, the #:info keyword supplies a procedure to be used by a get-info export (see read-language). The procedure produced by info-expr should consume three arguments: a key value, a default result, and a default info-getting procedure (to be called with the key and default result for default handling). If #:info is not supplied, the default info-getting procedure is used.

You can also use the (optional) module body forms to provide more definitions that might be needed to implement your reader functions. For example, here is a case-insensitive reader for the scheme/base language:

( module reader syntax/module-reader
scheme/base
#:read (wrap read ) #:read-syntax (wrap read-syntax )
( define ((wrap reader) . args)
( parameterize ([ read-case-sensitive #f]) ( apply reader args))))

In many cases, however, the standard read and read-syntax are fine, as long as you can customize the dynamic context they’re invoked at. For this, #:wrapper1 can specify a function that can control the dynamic context in which the reader functions are called. It should evaluate to a function that consumes a thunk and invokes it in the right context. Here is an alternative definition of the case-insensitive language using #:wrapper1:

( module reader syntax/module-reader
scheme/base
#:wrapper1 ( lambda (t)
( parameterize ([ read-case-sensitive #f])
(t))))

Note that using a readtable, you can implement languages that are extensions of plain S-expressions.

In addition to this wrapper, there is also #:wrapper2 that has more control over the resulting reader functions. If specified, this wrapper is handed the input port and a (one-argumet) reader function that expects the input port as an argument. This allows this wrapper to hand a different port value to the reader function, for example, it can divert the read to use different file (if given a port that corresponds to a file). Here is the case-insensitive implemented using this option:

( module reader syntax/module-reader
scheme/base
#:wrapper2 ( lambda (in r)
( parameterize ([ read-case-sensitive #f])
(r in))))

In some cases, the reader functions read the whole file, so there is no need to iterate them (e.g., Scribble’s read-inside and read-syntax-inside). In these cases you can specify #:whole-body-readers? as #t – the readers are expected to return a list of expressions in this case.

In addition, the two wrappers can return a different value than the wrapped function. This introduces two more customization points for the resulting readers:

- The thunk that is passed to a #:wrapper1 function reads the file contents and returns a list of read expressions (either syntax values or S-expressions). For 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
  scheme/base
  #:wrapper1 ( lambda (t) (t) '()))

  Note that it is still performing the read, otherwise the module loader will complain about extra expressions.

- The reader function that is passed to a #:wrapper2 function returns the final reault of the reader (a module expression). You can return a different value, for example, making it use a different language module.

In some rare cases, it is more convenient to know whether a reader is invoked for a read or for a read-syntax. To accommodate these cases, both wrappers can accept an additional argument, and in this case, they will be handed a boolean value that indicates whether the reader is expected to read syntax (#t) or not (#f). For example, here is a reader that uses the scribble syntax, and 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))

This 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))

Note: if such whole-body reader functions return a list with a single expression that begins with #%module-begin, then the syntax/module-reader language will not inappropriately add another. This for backwards-compatibility with older code: having a whole-body reader functions or wrapper functions that return a #%module-begin-wrapped body is deprectaed.

( 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))
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 #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.

( 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). 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)

top ← prev up next →