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.
#t if the syntax objects a and b refer to the
same lexically-bound identifier, or
#t if the syntax objects a and b refer to the
same free identifier, or
Return a list of temporary identifiers as long as ls is long.
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,
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
A parameter that indicates whether the expander should support legacy
syntax objects, as described above. For ABI stability reasons, the
parameterize to bind it to
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))
Return the name of the module whose source contains the identifier id.
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
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
to indicate that you are interested in syntax parameters. The value is
the default transformer procedure, as in
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
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
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) trans)) (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))))) #''aux))))))) (define-syntax foo (with-aux 'bar (syntax-rules () ((_) 'foo)))) (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.
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))