Chicken » Module (chicken syntax)

## Module (chicken syntax)

This module has support for syntax- and module handling. This module is used by default, unless a program is compiled with the `-explicit-use` option.

### Macro transformers

Macro transformers are procedures you can use in a `define-syntax` context to register a procedure that can transform s-expressions into other s-expressions. Only use these when you need to break hygiene in a controlled way; for many use cases `syntax-rules` is more appropriate, as it offers stronger guarantees of hygiene, is more high-level and is standard R5RS Scheme.

For those situations where you need more control, however, CHICKEN supports two kinds of low-level macros: so-called explicit renaming and implicit renaming macros.

#### Explicit renaming macros

The low-level macro facility that CHICKEN provides is called "explicit renaming" and allows writing hygienic or non-hygienic macros procedurally. When given the return value of one of the procedures `er-macro-transformer` or `ir-macro-transformer` instead of a `syntax-rules` form, `define-syntax` evaluates the procedure in a distinct expansion environment (initially having access to the exported identifiers of the `scheme` module). The procedure takes an expression and two other arguments and returns a transformed expression.

For example, the transformation procedure for a `call` macro such that `(call proc arg ...)` expands into `(proc arg ...)` can be written as

     (er-macro-transformer
       (lambda (exp rename compare)
         (cdr exp)))

Expressions are represented as lists in the traditional manner, except that identifiers are represented as special uninterned symbols.

The second argument to a transformation procedure is a renaming procedure that takes the representation of an identifier as its argument and returns the representation of a fresh identifier that occurs nowhere else in the program. For example, the transformation procedure for a simplified version of the `let` macro might be written as

     (er-macro-transformer
       (lambda (exp rename compare)
         (let ((vars (map car (cadr exp)))
               (inits (map cadr (cadr exp)))
               (body (cddr exp)))
           `((lambda ,vars ,@body)
             ,@inits))))

This would not be hygienic, however. A hygienic `let` macro must rename the identifier `lambda` to protect it from being captured by a local binding. The renaming effectively creates a fresh alias for `lambda`, one that cannot be captured by any subsequent binding:

     (er-macro-transformer
       (lambda (exp rename compare)
         (let ((vars (map car (cadr exp)))
               (inits (map cadr (cadr exp)))
               (body (cddr exp)))
           `((,(rename 'lambda) ,vars ,@body)
             ,@inits))))

The expression returned by the transformation procedure will be expanded in the syntactic environment obtained from the syntactic environment of the macro application by binding any fresh identifiers generated by the renaming procedure to the denotations of the original identifiers in the syntactic environment in which the macro was defined. This means that a renamed identifier will denote the same thing as the original identifier unless the transformation procedure that renamed the identifier placed an occurrence of it in a binding position.

Identifiers obtained from any two calls to the renaming procedure with the same argument will necessarily be the same, but will denote the same syntactical binding. It is an error if the renaming procedure is called after the transformation procedure has returned.

The third argument to a transformation procedure is a comparison predicate that takes the representations of two identifiers as its arguments and returns true if and only if they denote the same thing in the syntactic environment that will be used to expand the transformed macro application. For example, the transformation procedure for a simplified version of the `cond` macro can be written as

     (er-macro-transformer
       (lambda (exp rename compare)
         (let ((clauses (cdr exp)))
           (if (null? clauses)
               `(,(rename 'quote) unspecified)
               (let* ((first (car clauses))
                      (rest (cdr clauses))
                      (test (car first)))
                 (cond ((and (symbol? test)
                             (compare test (rename 'else)))
                        `(,(rename 'begin) ,@(cdr first)))
                       (else `(,(rename 'if)
                               ,test
                                (,(rename 'begin) ,@(cdr first))
                                (,(rename 'cond) ,@rest)))))))))

In this example the identifier `else` is renamed before being passed to the comparison predicate, so the comparison will be true if and only if the test expression is an identifier that denotes the same thing in the syntactic environment of the expression being transformed as `else` denotes in the syntactic environment in which the `cond` macro was defined. If `else` were not renamed before being passed to the comparison predicate, then it would match a local variable that happened to be named `else`, and the macro would not be hygienic. The final recursive call to `cond` also needs to be renamed because someone might create an alias for this macro and use it in a `let` where `cond` is an ordinary variable.

Some macros are non-hygienic by design. For example, the following defines a `loop` macro that implicitly binds `exit` to an escape procedure. The binding of `exit` is intended to capture free references to `exit` in the body of the loop, so `exit` is not renamed.

     (define-syntax loop
       (er-macro-transformer
         (lambda (x r c)
           (let ((body (cdr x)))
             `(,(r 'call-with-current-continuation)
               (,(r 'lambda) (exit)
                (,(r 'let) ,(r 'f) () ,@body (,(r 'f)))))))))

Suppose a `while` macro is implemented using `loop`, with the intent that `exit` may be used to escape from the `while` loop. The `while` macro cannot be written as

     (define-syntax while
       (syntax-rules ()
         ((while test body ...)
          (loop (if (not test) (exit #f))
                body ...))))

because the reference to `exit` that is inserted by the `while` macro is intended to be captured by the binding of `exit` that will be inserted by the `loop` macro. In other words, this `while` macro is not hygienic. Like `loop`, it must be written using procedurally:

     (define-syntax while
       (er-macro-transformer
         (lambda (x r c)
           (let ((test (cadr x))
                 (body (cddr x)))
             `(,(r 'loop)
               (,(r 'if) (,(r 'not) ,test) (exit #f))
               ,@body)))))

Think about it: If we did rename `exit`, it would refer to an `exit` procedure existing in the context of the macro's definition. That one actually exists; it is the procedure that exits the Scheme interpreter. Definitely not the one we want :) So now we make it refer to an `exit` that's locally bound in the environment where the macro is expanded.

Note: this implementation of explicit-renaming macros allows passing arbitrary expressions to the renaming and comparison procedures. When being renamed, a fresh copy of the expression will be produced, with all identifiers renamed appropriately. Comparison also supports arbitrary expressions as arguments.

##### er-macro-transformer

er-macro-transformer TRANSFORMERprocedure

  Returns an explicit-renaming macro transformer procedure created from the procedural macro body TRANSFORMER, which is a procedure of three arguments.

  This procedure will be called on expansion with the complete s-expression of the macro invocation, a rename procedure that hygienically renames identifiers and a comparison procedure that compares (possibly renamed) identifiers (see the section "Explicit renaming macros" below for a detailed explanation on non-R5RS macros).

  Implementation note: this procedure currently just returns its argument unchanged and is available for writing low-level macros in a more portable fashion, without hard-coding the signature of a transformer procedure.

### Implicit renaming macros

Explicit renaming macros generally require the user to perform quite a few renames, because most identifiers that aren't taken from the input expression should generally be inserted hygienically. It would make more sense to give the output expression as-is, and only explicitly convert those identifiers that you want to treat as unhygienic.

This can be done with implicit renaming macros. They just swap the default insertion "mode" from unhygienic to hygienic, so to speak. Here's the `cond` example from the previous section as an ir-macro:

     (ir-macro-transformer
       (lambda (exp inject compare)
         (let ((clauses (cdr exp)))
           (if (null? clauses)
               `(quote unspecified)
               (let* ((first (car clauses))
                      (rest (cdr clauses))
                      (test (car first)))
                 (cond ((and (symbol? test)
                             (compare test 'else))
                        `(begin ,@(cdr first)))
                       (else `(if ,test
                                  (begin ,@(cdr first))
                                  (cond ,@rest)))))))))

In this example the identifier `else` does not need to be renamed before being passed to the comparison predicate because it is already implicitly renamed. This comparison will also be true if and only if the test expression is an identifier that denotes the same thing in the syntactic environment of the expression being transformed as `else` denotes in the syntactic environment in which the `cond` macro was defined. If `else` were not renamed before being passed to the comparison predicate, then it would match a local variable that happened to be named `else`, and the macro would not be hygienic.

As you can see, the code is a lot clearer because it isn't obscured by excessive renaming.

Here's the `loop` macro so you can see how hygiene can be broken with implicit renaming macros:

     (define-syntax loop
       (ir-macro-transformer
         (lambda (expr inject compare)
           (let ((body (cdr expr)))
             `(call-with-current-continuation
               (lambda (,(inject 'exit))
                (let f () ,@body (f))))))))

The `while` macro is a little trickier: do we inject the call to `exit` or not? Just like the explicit renaming macro version did not rename it, we must inject it to allow it to be captured by the `loop` macro:

     (define-syntax while
       (ir-macro-transformer
         (lambda (expr inject compare)
           (let ((test (cadr expr))
                 (body (cddr expr)))
             `(loop
               (if (not ,test) (,(inject 'exit) #f))
               ,@body)))))

Note: Just like explicit renaming macros, this implementation of implicit renaming macros allow passing arbitrary expressions to the injection and comparison procedures. The injection procedure also return fresh copies of its input.

##### ir-macro-transformer

ir-macro-transformer TRANSFORMERprocedure

  This procedure accepts a reverse syntax transformer, also known as an implicit renaming macro transformer. This is a transformer which works almost like er-macro-transformer, except the rename and compare procedures it receives work a little differently.

  The rename procedure is now called `inject` and instead of renaming the identifier to be resolved in the macro's definition environment, it will explicitly inject the identifier to be resolved in the expansion environment. Any non-injected identifiers in the output expression produced by the transformer will be implicitly renamed to refer to the macro's environment instead. All identifiers in the input expression are of course implicitly injected just like with explicit renaming macros. See the section above for a more complete explanation.

  To compare an input identifier you can generally compare to the bare symbol and only free identifiers will match. In practice, this means that when you would call e.g. `(compare (cadr expression) (rename 'x))` in an ER macro, you simply call `(compare (cadr expression) 'x)` in the IR macro. Likewise, an unhygienic ER macro's comparison `(compare sym 'abc)` should be written as `(compare sym (inject 'abc))` in an IR macro.

### Expanding macros

#### expand

expand Xprocedure

  If X is a macro-form, expand the macro (and repeat expansion until expression is a non-macro form). Returns the resulting expression.

### Macro helper procedures

#### begin-for-syntax

`(begin-for-syntax EXP ...)`syntax

  Equivalent to `(begin EXP ...)`, but performs the evaluation of the expression during macro-expansion time, using the macro environment rather than the interaction environment.

  You can use this to define your own helper procedures that you can call from a syntax transformer.

#### define-for-syntax

`(define-for-syntax (NAME VAR ...) EXP1 ...)`syntax
`(define-for-syntax (NAME VAR1 ... VARn . VARn+1) EXP1 ...)`syntax
`(define-for-syntax NAME [VALUE])`syntax

  Defines the toplevel variable `NAME` at macro-expansion time. This can be helpful when you want to define support procedures for use in macro-transformers, for example.

  Essentially, this is a shorthand for `(begin-for-syntax (define ...))`.

  Note that `define-for-syntax` definitions within a module are implicitly added to that module's import library. Refer to the documentation on import libraries for more information.

#### syntax

syntax EXPRESSIONprocedure

  This will quote the EXPRESSION for use in a syntax expansion. Any syntactic information will be stripped from the EXPRESSION.

#### strip-syntax

strip-syntax EXPRESSIONprocedure

  Strips all syntactical information from EXPRESSION, returning a new expression where symbols have all context-information removed.

  You should use this procedure whenever you want to manually construct new identifiers, which an unhygienic macro can insert. In some cases it does not appear to be necessary to strip context information when you use the macro, but you still should do it. Sometimes identifiers will not have been renamed (most often at toplevel), but there may be other contexts in which identifiers will have been renamed.

#### read-with-source-info

read-with-source-info #!optional portprocedure

  Exactly like `{read`} from the `{scheme`} module, except it registers the expression it read into the line number database, so that if `{(read-with-source-info)`} returns `{OBJ`}, `{(get-line-number OBJ)`} will return the line number in `{port`}.

  The port argument may be omitted, in which case it defaults to the value returned by `current-input-port`. It is an error to read from a closed port.

#### get-line-number

get-line-number EXPRprocedure

  If EXPR is a pair with the car being a symbol, and line-number information is available for this expression, then this procedure returns the associated source file and line number as a string. If line-number information is not available, then `#f` is returned.

#### syntax-error

`(syntax-error [LOCATION] MESSAGE ARGUMENT ...)`procedure

  Signals an exception of the kind `(exn syntax)`. Otherwise identical to `error`.

### Compiler macros

#### define-compiler-syntax

`(define-compiler-syntax NAME)`syntax
`(define-compiler-syntax NAME TRANSFORMER)`syntax

  Defines what is usually called a compiler macro in Lisp: `NAME` should be the name of a globally or locally bound procedure. Any direct call to this procedure will be transformed before compilation, which allows arbitrary rewritings of function calls.

  `TRANSFORMER` can be a `syntax-rules` expression or a transformer procedure (as returned by `er-macro-transformer` or `ir-macro-transformer`). Returning the original form in an explicit/implicit-renaming macro or simply "falling trough" all patterns in a `syntax-rules` form will keep the original expression and compile it normally.

  In the interpreter this form does nothing and returns an unspecified value.

  Compiler-syntax is always local to the current compilation unit and can not be exported. Compiler-syntax defined inside a module is not visible outside of that module.

  `define-compiler-syntax` should only be used at top-level. Local compiler-syntax can be defined with `let-compiler-syntax`.

      (define-compiler-syntax +
        (syntax-rules ()
          ((_) 1)
          ((_ x 0) x) ) )

  If no transformer is given, then `(define-compiler-syntax NAME)` removes any compiler-syntax definitions for `NAME`.

#### let-compiler-syntax

`(let-compiler-syntax ((NAME [TRANSFORMER]) ...) BODY ...)`syntax

  Allows definition local compiler macros, which are only applicable inside `BODY ...`. By not providing a `TRANSFORMER` expression, compiler-syntax for specific identifiers can be temporarily disabled.

Previous: Module (chicken string)

Next: Module (chicken tcp)