Next: expander intro lex, Previous: expander intro times, Up: expander intro [Index]
A syntactic binding is an association between an identifier and the result of evaluating an expression; there are different kinds:
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.
Associations between identifiers and expressions evaluated at run–time.
They are established by define
, let
, letrec
and similar.
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).
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.
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.
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:
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.
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
.
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: expander intro lex, Previous: expander intro times, Up: expander intro [Index]