6.16.3 Reading Scheme Code, For the Compiler

When something goes wrong with a Scheme program, the user will want to know how to fix it. This starts with identifying where the error occured: we want to associate a source location with each component part of source code, and propagate that source location information through to the compiler or interpreter.

For that, Guile provides read-syntax.

Scheme Procedure: read-syntax [port]

Read an s-expression from the input port port, or from the current input port if port is not specified.

If, after skipping white space and comments, no more bytes are available from port, return the end-of-file object. See Binary I/O. Otherwise, return an annotated datum. An annotated datum is a syntax object which associates a source location with a datum. For example:

(call-with-input-string "  foo" read-syntax)
; ⇒ #<syntax:unknown file:1:2 foo>
(call-with-input-string "(foo)" read-syntax)
; ⇒
; #<syntax:unknown file:1:0
;   (#<syntax unknown file:1:1 foo>)>

As the second example shows, all fields of pairs and vectors are also annotated, recursively.

Most users are familiar with syntax objects in the context of macros, which use syntax objects to associate scope information with identifiers. See Macros. Here we use syntax objects to associate source location information with any datum, but without attaching scope information. The Scheme compiler (compile) and the interpreter (eval) can accept syntax objects directly as input, allowing them to associate source information with resulting code. See Compiling Scheme Code, and See Procedures for On the Fly Evaluation.

Note that there is a legacy interface for getting source locations into the Scheme compiler or interpreter, which is to use a side table that associates “source properties” with each subdatum returned by read, instead of wrapping the datums directly as in read-syntax. This has the disadvantage of not being able to annotate all kinds of datums. See Source Properties, for more information.