Next: , Previous: , Up: expander intro   [Index]


15.1.3 Syntactic bindings

A syntactic binding is an association between an identifier and the result of evaluating an expression; there are different kinds:

primitive syntax bindings

Syntactic forms built into the language, they are called primitive core macros and primitive non–core macros. They are lambda, define, let and similar, define-syntax, letrec-syntax, if, and, or and all the rest.

variable bindings

Associations between identifiers and expressions evaluated at run–time. They are established by define, let, letrec and similar.

keyword bindings

Associations between identifiers and expressions evaluated at expand–time. The identifiers in this role are called syntactic keywords (not to be confused with keyword objects like ‘#:hello’). Examples of keyword bindings are user–defined macros (also named user–defined syntaxes).

Identifiers in binding position and reference position

Let’s consider the following syntactic form:

(let ((a 1))
  a)

the let syntax defines a variable binding whose name is ‘a’; a variable binding is an association between a name and the result of evaluating an expression at run–time.

Let’s consider the following form:

(let-syntax ((a (identifier-syntax 1)))
  a)

let-syntax defines a keyword binding whose name is ‘a’; a keyword binding is an association between a name and the result of evaluating an expression at expand–time.

The identifiers ‘a’ in the first argument of let and let-syntax are in binding position; the identifiers in the bodies of let and let-syntax are in reference position. The identifier in reference position is a reference to the identifier in binding position: we say that the identifier in reference position is “captured” by the identifier in binding position.

Lexical environment

The expansion process proceeds from the outer forms to the inner forms. Given the syntactic form:

(let ((a 1))
  (let ((b 2))
    (+ a b)))

first the outer let is processed and the syntactic binding for ‘a’ is established; then the inner let is processed and the syntactic binding for ‘b’ is established; finally the expression (+ a b) is processed in a lexical context in which the syntactic bindings exist.

The region of the syntactic binding ‘a’ is the inner let form; notice that the binding for ‘a’ is not visible in the right–hand side of its definition. The region of the syntactic binding ‘b’ is the internal expression.

During the expansion process: syntactic bindings are added to an internal data structure that collects the associations, keeping track of nested regions of visibility: the lexical environment. The lexical environment is somewhat handled like a stack: while entering internal expressions, syntactic binding descriptors are pushed on the stack.

Initial environment, top level bindings, local bindings

Let’s consider the following library:

(library (demo)
  (export)
  (import (rnrs (6)))

  (define var 1)

  (define (fun a)
    (let ((b 2))
      (let-syntax ((c (identifier-syntax 3)))
        (display (+ a b c))
        (newline))))

  (define-syntax syn
    (syntax-rules ()
      ((_ ?obj)
       (display ?obj))))

  (import (prefix (vicare posix) px.))
  (display (px.getenv "PATH"))
  (newline)

  #| end of library |# )

we introduce the definitions:

initial lexical environment

The syntactic bindings imported by the import clause of the library form and by the import syntax in the body of the library, constitute the initial lexical environment for the expansion process of the library.

In the example: the initial environment is the set of bindings exported by the libraries (rnrs (6)) and (vicare posix).

NOTE An import syntax that appears in an internal body as in:

(lambda ()
  (import (srfi :19))
  (do-something))

does not add bindings to the initial environment: it adds them to the local lexical environment.

top level syntactic bindings

Syntactic bindings defined in the body of the library with define and define-syntax; the ones defined by define are variable bindings, the ones defined by define-syntax are keyword bindings.

Top level bindings are visible in the whole library body: their region is the whole body. Top level bindings have indefinite extent: their values are never garbage collected, they exists for the whole life of the vicare process.

In the example: ‘var’ and ‘fun’ are top level variables; ‘syn’ is a top level keyword.

Every identifier in reference position that is not captured by a syntactic binding definition in the library itself must be captured by a binding in the initial environment; otherwise an exception is raised with condition object of type &undefined.

local syntactic bindings

Syntactic bindings defined by a syntax that limits their region of visibility to a subform of the library body. Bindings defined by let and similar syntaxes are local variable bindings, as well as those defined by define in an internal body. Bindings defined by let-syntax and similar syntaxes are local keyword bindings, as well as those defined by define-syntax in an internal body.

In the example: ‘a’ and ‘b’ are local variable bindings; ‘c’ is a local keyword binding.


Next: , Previous: , Up: expander intro   [Index]