Next: , Previous: , Up: Macros   [Contents][Index]

6.10.4 Syntax Transformer Helpers

As noted in the previous section, Guile’s syntax expander operates on syntax objects. Procedural macros consume and produce syntax objects. This section describes some of the auxiliary helpers that procedural macros can use to compare, generate, and query objects of this data type.

Scheme Procedure: bound-identifier=? a b

Return #t if the syntax objects a and b refer to the same lexically-bound identifier, or #f otherwise.

Scheme Procedure: free-identifier=? a b

Return #t if the syntax objects a and b refer to the same free identifier, or #f otherwise.

Scheme Procedure: generate-temporaries ls

Return a list of temporary identifiers as long as ls is long.

Scheme Procedure: syntax-source x

Return the source properties that correspond to the syntax object x. See Source Properties, for more information.

And now, a bit of confession time. Guile’s syntax expander originates in code from Chez Scheme: a version of the expander in Chez Scheme that was made portable to other Scheme systems. Way back in the mid-1990s, some Scheme systems didn’t even have the ability to define new abstract data types. For this reason, the portable expander from Chez Scheme that Guile inherited used tagged vectors as syntax objects: vectors whose first element was the symbol, syntax-object.

At the time of this writing it is 2017 and Guile still has support for this strategy. It worked for this long because no one ever puts a literal vector in the operator position:

(#(syntax-object ...) 1 2 3)

But this state of affairs was an error. Because syntax objects are just vectors, this makes it possible for any Scheme code to forge a syntax object which might cause it to violate abstraction boundaries. You can’t build a sandboxing facility that limits the set of bindings in scope when one can always escape that limit just by evaluating a special vector. To fix this problem, Guile 2.2.1 finally migrated to represent syntax objects as a distinct type with a distinct constructor that is unavailable to user code.

However, Guile still has to support “legacy” syntax objects, because it could be that a file compiled with Guile 2.2.0 embeds syntax objects of the vector kind. Whether the expander treats the special tagged vectors as syntax objects is now controllable by the allow-legacy-syntax-objects? parameter:

Scheme Procedure: allow-legacy-syntax-objects?

A parameter that indicates whether the expander should support legacy syntax objects, as described above. For ABI stability reasons, the default is #t. Use parameterize to bind it to #f. See Parameters.

Guile also offers some more experimental interfaces in a separate module. As was the case with the Large Hadron Collider, it is unclear to our senior macrologists whether adding these interfaces will result in awesomeness or in the destruction of Guile via the creation of a singularity. We will preserve their functionality through the 2.0 series, but we reserve the right to modify them in a future stable series, to a more than usual degree.

(use-modules (system syntax))
Scheme Procedure: syntax-module id

Return the name of the module whose source contains the identifier id.

Scheme Procedure: syntax-local-binding id [#:resolve-syntax-parameters?=#t]

Resolve the identifer id, a syntax object, within the current lexical environment, and return two values, the binding type and a binding value. The binding type is a symbol, which may be one of the following:


A lexically-bound variable. The value is a unique token (in the sense of eq?) identifying this binding.


A syntax transformer, either local or global. The value is the transformer procedure.


A syntax parameter (see Syntax Parameters). By default, syntax-local-binding will resolve syntax parameters, so that this value will not be returned. Pass #:resolve-syntax-parameters? #f to indicate that you are interested in syntax parameters. The value is the default transformer procedure, as in macro.


A pattern variable, bound via syntax-case. The value is an opaque object, internal to the expander.


An internal binding, bound via with-ellipsis. The value is the (anti-marked) local ellipsis identifier.


A lexical variable that has gone out of scope. This can happen if a badly-written procedural macro saves a syntax object, then attempts to introduce it in a context in which it is unbound. The value is #f.


A global binding. The value is a pair, whose head is the symbol, and whose tail is the name of the module in which to resolve the symbol.


Some other binding, like lambda or other core bindings. The value is #f.

This is a very low-level procedure, with limited uses. One case in which it is useful is to build abstractions that associate auxiliary information with macros:

(define aux-property (make-object-property))
(define-syntax-rule (with-aux aux value)
  (let ((trans value))
    (set! (aux-property trans) aux)
(define-syntax retrieve-aux
  (lambda (x)
    (syntax-case x ()
      ((x id)
       (call-with-values (lambda () (syntax-local-binding #'id))
         (lambda (type val)
           (with-syntax ((aux (datum->syntax #'here
                                             (and (eq? type 'macro)
                                                  (aux-property val)))))
(define-syntax foo
  (with-aux 'bar
    (syntax-rules () ((_) 'foo))))
⇒ foo
(retrieve-aux foo)
⇒ bar

syntax-local-binding must be called within the dynamic extent of a syntax transformer; to call it otherwise will signal an error.

Scheme Procedure: syntax-locally-bound-identifiers id

Return a list of identifiers that were visible lexically when the identifier id was created, in order from outermost to innermost.

This procedure is intended to be used in specialized procedural macros, to provide a macro with the set of bound identifiers that the macro can reference.

As a technical implementation detail, the identifiers returned by syntax-locally-bound-identifiers will be anti-marked, like the syntax object that is given as input to a macro. This is to signal to the macro expander that these bindings were present in the original source, and do not need to be hygienically renamed, as would be the case with other introduced identifiers. See the discussion of hygiene in section 12.1 of the R6RS, for more information on marks.

(define (local-lexicals id)
  (filter (lambda (x)
            (eq? (syntax-local-binding x) 'lexical))
          (syntax-locally-bound-identifiers id)))
(define-syntax lexicals
  (lambda (x)
    (syntax-case x ()
      ((lexicals) #'(lexicals lexicals))
      ((lexicals scope)
       (with-syntax (((id ...) (local-lexicals #'scope)))
         #'(list (cons 'id id) ...))))))

(let* ((x 10) (x 20)) (lexicals))
⇒ ((x . 10) (x . 20))

Next: , Previous: , Up: Macros   [Contents][Index]