Parse-Declarations API Documentation

Table of Contents


Next: , Previous: (dir), Up: (dir)

Parse-Declarations API Documentation

Copyright © 2008 Tobias C. Rittweiler <trittweiler at common-lisp.net>

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

The software is provided “as is”, without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the software or the use or other dealings in the software.


Next: , Previous: Top, Up: Top

1 Introduction

Parse-Declarations is a Common Lisp library to help writing macros which establish bindings. To be semantically correct, such macros must take user declarations into account, as these may affect the bindings they establish. Yet the ANSI standard of Common Lisp does not provide any operators to work with declarations in a convenient, high-level way.

This library provides such operators. In particular, it includes facilities to

All declaration specifiers defined by the ANSI standard are understood. Furthermore, Parse-Declarations provides ways for the user to customize how unknown declaration specifiers are parsed. In particular, it allows users to


Next: , Previous: Introduction, Up: Top

2 API

The API.


Next: , Previous: API, Up: API

analyze-declaration-specifier

Syntax

— Generic Function: analyze-declaration-specifier declaration-identifier declaration-args compilation-env

⇒ result-identifier, result-args, context

Method signatures

— Method: analyze-declaration-specifier (id (eql 'declaration)) args env
— Method: analyze-declaration-specifier (id (eql 'dynamic-extent)) args env
— Method: analyze-declaration-specifier (id (eql 'ftype)) args env
— Method: analyze-declaration-specifier (id (eql 'ignore)) args env
— Method: analyze-declaration-specifier (id (eql 'ignorable)) args env
— Method: analyze-declaration-specifier (id (eql 'inline)) args env
— Method: analyze-declaration-specifier (id (eql 'notinline)) args env
— Method: analyze-declaration-specifier (id (eql 'optimize)) args env
— Method: analyze-declaration-specifier (id (eql 'special)) args env
— Method: analyze-declaration-specifier (id (eql 'type)) args env
— Method: analyze-declaration-specifier (id class) args env
— Method: analyze-declaration-specifier (id cons) args env
— Method: analyze-declaration-specifier (id symbol) args env
— Method: analyze-declaration-specifier id args env

Arguments and Values

declaration-identifier
A declaration identifier.
declaration-args
A list of declaration arguments.
compilation-env
An environment object.
result-identifier
A declaration identifier.
result-args
A list of declaration arguments.
context
An object.

Description

The generic function analyze-declaration-specifier is used by parse-declarations to split an arbitrary declaration specifier into semantically-interesting parts. These parts are returned as multiple values.

At the moment, the following three values are returned:

  1. The declaration identifier of the declaration specifier. This may be a different identifier than the one analyze-declaration-specifier has been called with, for normalization purposes.
  2. A list of normalized binding names that are affected by the declaration specifier.
  3. An arbitrary object called the “context” that is used by build-declaration-specifier, along with the other two return values above, to reconstruct the declaration specifier.

Examples

  PARSE-DECLARATIONS> (analyze-declaration-specifier 'optimize '((speed 0) debug) nil)
  => OPTIMIZE
  => NIL
  => ((SPEED 0) DEBUG)
  PARSE-DECLARATIONS> (analyze-declaration-specifier 'type '(fixnum x y z) nil)
  => TYPE
  => (X Y Z)
  => FIXNUM
  PARSE-DECLARATIONS> (analyze-declaration-specifier 'inline '(f g h) nil)
  => INLINE
  => (#'F #'G #'H)
  => NIL
  PARSE-DECLARATIONS> (analyze-declaration-specifier '(string 512) '(str1 str2) nil)
  => TYPE
  => (STR1 STR2)
  => (STRING 512)

Exceptional Situations

None.

See Also

build-declaration-specifier
parse-declarations

Notes

The compilation-env is provided for methods that have to deal with type specifiers. Cf. X3J13 Issue #334.

The default method of analyze-declaration-specifier will construct an unknown declaration specifier whose “context” is the passed declaration-args. This is mostly interesting to know for usages of map-declaration-env.

Users of the library Parse-Declarations can extend the set of understood declaration specifiers by adding methods to the generic-functions analyze-declaration-specifier and build-declaration-specifier. These methods should most likely specialize on the first argument with an EQL specializer.


Next: , Previous: analyze-declaration-specifier, Up: API

build-declaration-specifier

Syntax

— Generic Function: build-declaration-specifier declaration-identifier affected-variables context

⇒ result-specifier

Method signatures

— Method: build-declaration-specifier (id (eql 'declaration)) vars context
— Method: build-declaration-specifier (id (eql 'dynamic-extent)) vars context
— Method: build-declaration-specifier (id (eql 'ftype)) vars context
— Method: build-declaration-specifier (id (eql 'ignore)) vars context
— Method: build-declaration-specifier (id (eql 'ignorable)) vars context
— Method: build-declaration-specifier (id (eql 'inline)) vars context
— Method: build-declaration-specifier (id (eql 'notinline)) vars context
— Method: build-declaration-specifier (id (eql 'optimize)) vars context
— Method: build-declaration-specifier (id (eql 'special)) vars context
— Method: build-declaration-specifier (id (eql 'type)) vars context
— Method: build-declaration-specifier id vars context

Arguments and Values

declaration-identifier
A declaration identifier.
affected-variables
A list of normalized binding names affected by the declaration specifier under construction.
context
An object.
result-specifier
A declaration specifier.

Description

The generic function build-declaration-specifier is used by build-declarations to reconstruct a declaration specifier from the parts returned by analyze-declaration-specifier.

It is hence the counterpart of analyze-declaration-specifier: whereas that one disassembles a declaration specifier into parts, build-declaration-specifier assembles the parts back into a declaration specifier.

Examples

  PARSE-DECLARATIONS> (build-declaration-specifier 'optimize nil '((speed 0) debug))
  => (OPTIMIZE (SPEED 0) DEBUG)
  PARSE-DECLARATIONS> (build-declaration-specifier 'type '(x y z) 'fixnum)
  => (TYPE FIXNUM X Y Z)
  PARSE-DECLARATIONS> (build-declaration-specifier 'inline '(#'f #'g #'h) nil)
  => (INLINE F G H)

Exceptional Situations

None.

See Also

analyze-declaration-specifier
build-declarations

Notes

build-declaration-specifier can be thought of as being (almost) the inverse function of analyze-declaration-specifier. That is for an arbitrary declaration specifier in *SPEC*, the following code should axiomatically result in a declaration specifier that is equivalent to *SPEC* when interpreted by the Common Lisp implementation:

  (multiple-value-call #'build-declaration-specifier
    (analyze-declaration-specifier (first *spec*) (rest *spec*) nil))

However, the result may in fact not be EQUAL to the original specifier, as analyze-declaration-specifier can perform arbitrary normalization:

  PARSE-DECLARATIONS> (let ((spec '((string 512) variable)))
                        (multiple-value-call #'build-declaration-specifier
                          (analyze-declaration-specifier (first spec) (rest spec) nil)))
  => (TYPE (STRING 512) VARIABLE)


Next: , Previous: build-declaration-specifier, Up: API

build-declarations

Syntax

— Function: build-declarations tag &rest declaration-envs+ ⇒ declarations-or-specifiers

Arguments and Values

tag
A symbol.
declaration-env
A declaration-env
declarations-or-specifiers
Either a list of declarations, or a list of declaration specifiers.

Description

The function build-declarations constructs all the declarations that are stored in each of the declaration-envs. The symbol tag is used as the first element of these declarations. If tag is NIL, build-declarations returns a list of declaration specifiers rather than declarations.

The order of the returned expressions is not specified; in particular, the order doesn't have to be in any relation with the order of declarations that parse-declarations has been invoked with to create one of declaration-envs.

Furthermore, declarations-or-specifiers may share structure with the declaration specifiers that were initially passed to parse-declarations.

Examples

  PARSE-DECLARATIONS> (defparameter *env* (parse-declarations '((declare (optimize speed))
                                                                (declare (fixnum x y))
                                                                (declare (inline +)))))
  => *ENV*
  PARSE-DECLARATIONS> (build-declarations 'declare *env*)
  => ((DECLARE (INLINE +)) (DECLARE (TYPE FIXNUM X Y)) (DECLARE (OPTIMIZE SPEED)))
  PARSE-DECLARATIONS> (build-declarations nil *env*)
  => ((TYPE FIXNUM X Y) (INLINE +) (OPTIMIZE SPEED))

Exceptional Situations

Signals an error of type TYPE-ERROR if tag is not a symbol, or if any declaration-env is not a declaration-env.

See Also

build-declaration-specifier
parse-declarations


Next: , Previous: build-declarations, Up: API

check-declaration-env

Syntax

— Macro: check-declaration-env place &key unknown-allowed warn-only ⇒ declaration-env

Arguments and Values

place
A place which contains a declaration-env.
unknown-allowed
A boolean.
warn-only
A boolean.
declaration-env
A declaration-env.

Description

Checks that place is a declaration-env with certain properties.

If unknown-allowed is NIL, and declaration-env contains unknown declaration specifiers, an continuable error is signalled. If the CONTINUE restart is invoked, place is set to an declaration-env with all the unknown declaration specifiers filtered away.

If warn-only is true, warnings instead of errors are signalled, and check-declaration-env behaves as if the CONTINUE restart was invoked.

Exceptional Situations

Signals an error of type TYPE-ERROR if declaration-env is not a declaration-env.

Signals continuable errors, or warnings, under the circumstances described above.

See Also

filter-declaration-env


Next: , Previous: check-declaration-env, Up: API

declaration-env

Syntax

— Class: declaration-env

Class Precedence List

declaration-env, t

Description

A declaration-env is a container for declaration specifiers.

See Also

check-declaration-env
declaration-env-p
declaration-env.affected-variables
declaration-env.policy
parse-declarations

Notes

At the discretion of the implementation, either standard-object or structure-object might appear in the class precedence list. Cf. CLHS 4.2.2.

The type declaration-env is opaque, there is no explicit constructor provided. Use parse-declarations instead.


Next: , Previous: declaration-env, Up: API

declaration-env-p

Syntax

— Function: declaration-env-p declaration-env ⇒ bool

Arguments and Values

declaration-env
A declaration-env
bool
A boolean.

Description

Returns true if declaration-env is a declaration-env, false otherwise.

Exceptional Situations

None.

See Also

declaration-env


Next: , Previous: declaration-env-p, Up: API

declaration-env.affected-variables

Syntax

— Function: declaration-env.affected-variables declaration-env &optional allowed-decl-ids ⇒ binding-names

Arguments and Values

declaration-env
A declaration-env
allowed-decl-ids
A list of normalized declaration identifiers.
binding-names
A list of normalized binding names.

Description

The function declaration-env.affected-variables returns all binding names that are affected by the declaration specifiers stored in declaration-env. If allowed-decl-ids is given, only the binding names affected by the specifiers starting with one of allowed-decl-ids are returned.

Examples

  PARSE-DECLARATIONS> (defparameter *env* (parse-declarations '((declare (optimize speed))
                                                                (declare (fixnum x y))
                                                                (declare (inline +)))))
  => *ENV*
  PARSE-DECLARATIONS> (declaration-env.affected-variables *env*)
  => (X Y #'+)
  PARSE-DECLARATIONS> (declaration-env.affected-variables *env* '(type))
  => (X Y)
  
  ;;; The following returns NIL, as (ANALYZE-DECLARATION-SPECIFIER 'FIXNUM NIL NIL)
  ;;; returns TYPE as first value.
  PARSE-DECLARATIONS> (declaration-env.affected-variables *env* '(fixnum))
  => NIL
  

Exceptional Situations

Signals an error of type TYPE-ERROR if declaration-env is not a declaration-env.

See Also

analyze-declaration-specifier
declaration-env
declaration-env.policy


Next: , Previous: declaration-env.affected-variables, Up: API

declaration-env.policy

Syntax

— Function: declaration-env.policy declaration-env ⇒ policy

Arguments and Values

declaration-env
A declaration-env
policy
A list of optimize qualities.

Description

The function declaration-env.policy returns the optimize qualities that are stored in declaration-env.

Examples

  PARSE-DECLARATIONS> (declaration-env.policy (parse-declarations
                                               '((declare (optimize (speed 0) (debug 2)))
                                                 (declare (fixnum x y))
                                                 (declare (inline +))
                                                 (declae (optimize (safety 3))))))
  => ((SAFETY 3) (SPEED 0) (DEBUG 2))

Exceptional Situations

Signals an error of type TYPE-ERROR if declaration-env is not a declaration-env.

See Also

declaration-env
declaration-env.affected-variables


Next: , Previous: declaration-env.policy, Up: API

filter-declaration-env

Syntax

— Function: filter-declaration-env declenv &key affecting not-affecting (include :everything) exclude filter-function

⇒ result-env

Arguments and Values

declenv
A declaration-env.
affecting
A list of normalized binding names.
not-affecting
A list of normalized binding names.
include
Either one of the keywords :everything, :bound, :free, :unknown, or a list of normalized declaration identifiers.
exclude
Either one of the keywords :everything, :bound, :free, :unknown, or a list of normalized declaration identifiers.
filter-function
A function designator for a function with three arguments.
result-env
A fresh declaration-env.

Description

The function filter-declaration-env returns a new declaration-env containing a subset of the declaration specifiers in declenv according to a filtering constituted by the given parameters.

include specifies the declaration specifiers to be considered by the filtering. They can be specified either directly via a list of identifiers, or via a keyword representing a certain set of specifiers. These keywords are:

:bound
Represents the bound declaration specifiers in declenv
:everything
Represents all declaration specifiers in declenv.
:free
Represents the free declaration specifiers in declenv,
:unknown
Represents the unknown declaration specifiers in declenv.

exclude specifies the declaration specifiers to be not considered.

affecting, if given, specifies the set of binding names that the declaration specifiers in the resulting declaration-env must affect. Consequently, only bound declaration specifiers are selected.

not-affecting, if given, specifies the set of binding names that the resulting specifiers must not affect. This possibly includes free declaration specifiers and unknown declaration specifiers

filter-function is called for each declaration specifier in declenv that satisfies the other given parameters. It should return true if this specifier should be included in the resulting declaration-env. The function is invoked with the disassembled parts of this specifier, cf. analyze-declaration-specifier.

Examples

  PARSE-DECLARATIONS> (defparameter *sample-decls*
                        '((declare (optimize (speed 3) (safety 0)))
                          (declare (special *a*) (special *f*))
                          (declare (inline f))
                          (declare (author "Tobias C Rittweiler"))
                          (declare (type integer x y))
                          (declare (optimize (debug 0)))
                          (declare (type fixnum z))
                          (declare ((string 512) output))
                          (declare (type (vector unsigned-byte 32) chunk))
                          (declare (quux *a*))   ; assuming QUUX hasn't been defined as type.
                          (declare (float *f*))
                          (declare (ftype (function (number) float) f))
                          ))
  => *SAMPLE-DECLS*
  PARSE-DECLARATIONS> (defparameter *env* (parse-declarations *sample-decls*))
  => *ENV*
  
  PARSE-DECLARATIONS> (build-declarations 'declare (filter-declaration-env *env* :include :free))
  => ((DECLARE (OPTIMIZE (DEBUG 0))) (DECLARE (OPTIMIZE (SPEED 3) (SAFETY 0))))
  
  PARSE-DECLARATIONS> (build-declarations 'declare (filter-declaration-env *env* :include :unknown))
  => ((DECLARE (QUUX *A*)) (DECLARE (AUTHOR "Tobias C Rittweiler")))
  
  PARSE-DECLARATIONS> (build-declarations 'declare (filter-declaration-env *env* :affecting '(*a*)))
  => ((DECLARE (SPECIAL *A*)))
  
  PARSE-DECLARATIONS> (build-declarations 'declare (filter-declaration-env *env* :affecting '(*f*)))
  => ((DECLARE (TYPE FLOAT *F*)) (DECLARE (SPECIAL *F*)))
  
  PARSE-DECLARATIONS> (build-declarations 'declare (filter-declaration-env *env* :affecting '(#'f)))
  => ((DECLARE (INLINE F)) (DECLARE (FTYPE (FUNCTION (NUMBER) FLOAT) F)))
  
  PARSE-DECLARATIONS> (build-declarations 'declare (filter-declaration-env *env*
                                                      :affecting '(#'f)
                                                      :exclude '(inline notinline)))
  => ((DECLARE (FTYPE (FUNCTION (NUMBER) FLOAT) F)))
  
  PARSE-DECLARATIONS> (build-declarations 'declare (filter-declaration-env *env* :include '(type)))
  => ((DECLARE (TYPE FLOAT *F*)) (DECLARE (TYPE (VECTOR UNSIGNED-BYTE 32) CHUNK))
      (DECLARE (TYPE (STRING 512) OUTPUT)) (DECLARE (TYPE FIXNUM Z))
      (DECLARE (TYPE INTEGER X Y)))
  

Exceptional Situations

Signals an error of type TYPE-ERROR if a passed argument violates its respective entry in the “Arguments and Values” section.

See Also

declaration-env.affected-variables
map-declaration-env

Notes

If no &key parameter is given, filter-declaration-env will return a copy of the given declaration-env. This follows from the default values of the parameters.


Next: , Previous: filter-declaration-env, Up: API

map-declaration-env

Syntax

— Function: map-declaration-env function declaration-env ⇒ result-env

Arguments and Values

function
A function designator for a function with three arguments.
declaration-env
A declaration-env.
result-env
A fresh declaration-env.

Description

The function map-declaration-env maps function over declaration-env in the following way: function is called for each declaration specifier in declaration-env with the parts (cf. analyze-declaration-specifier) of that specifier. function should either return new values for the parts, or return the same values.

If new values are returned for an unknown declaration specifier, the specifier ceases to be considered unknown in result-env.

map-declaration-env cannot be used to filter particular entries of declaration-env away.

Exceptional Situations

Signals an error of type TYPE-ERROR if function is not a function designator, or declaration-env is not a declaration-env.

See Also

filter-declaration-env

Notes

map-declaration-env is provided to let users of the library Parse-Declaration customize the way declaration specifiers are parsed in arbitrary ways that scale even when multiple applications use Parse-Declarations in the same Lisp image.

Users can extend the set of understood declaration specifiers by adding methods on analyze-declaration-specifier and build-declaration-specifier. These methods ought to specialize on the declaration identifier with an EQL specializer. The package machinery will ensure that no conflict will arise between multiple applications in the same Lisp image.

If users want to customize the behaviour beyond that (for example parsing a standard declaration specifier in a different way), they should locally use map-declaration-env on the result of parse-declarations.


Next: , Previous: map-declaration-env, Up: API

merge-declaration-envs

Syntax

— Function: merge-declaration-envs declaration-env1 declaration-env2 ⇒ result-env

Arguments and Values

declaration-env1
A declaration-env.
declaration-env2
A declaration-env.
result-env
A fresh declaration-env.

Description

The function merge-declaration-envs returns the union of the declaration specifiers in declaration-env1 and declaration-env2 as a new declaration-env. There is no attempt made at removing duplicates, or at any other kind of normalization.

Examples

  PARSE-DECLARATIONS> (defparameter *env* (parse-declarations '((declare (optimize speed))
                                                                (declare (fixnum x y))
                                                                (declare (inline +)))))
  => *ENV*
  PARSE-DECLARATIONS> (defparameter *env2* (parse-declarations '((declare (type fixnum y z))
                                                                 (declare (notinline +))
                                                                 (declare (optimize (safety 3))))))
  => *ENV2*
  PARSE-DECLARATIONS> (build-declarations 'declare (merge-declaration-envs *env* *env2*))
  => ((DECLARE (NOTINLINE +))
             (DECLARE (INLINE +))
             (DECLARE (TYPE FIXNUM X Y))
             (DECLARE (TYPE FIXNUM Y Z))
             (DECLARE (OPTIMIZE SPEED))
             (DECLARE (OPTIMIZE (SAFETY 3))))

Exceptional Situations

Signals an error of type TYPE-ERROR if declaration-env1 or declaration-env2 is not a declaration-env.


Next: , Previous: merge-declaration-envs, Up: API

parse-body

Syntax

— Function: parse-body body-and-decls &key documentation whole ⇒ body, declarations, docstring

Arguments and Values

body-and-decls
A list which can contains declarations, a documentation string, and forms.
documentation
A boolean.
whole
An object.
body
A list of forms.
declarations
A list of declarations.
docstring
A documentation string.

Description

The function parse-body splits an &BODY argument of an extended lambda list into its three distinctive parts which are returned as multiple values:

  1. The actual body forms supposed to be evaluated.
  2. The declarations if any.
  3. The documentation string if any.

A documentation string is only parsed if documentation is true. whole is used to indicate the context if an error is signalled, and should hence be a symbol naming the operator parse-body is used to define.

Examples

  PARSE-DECLARATIONS> (parse-body '("Also sprach Zarathustra.."
                                    (declare (optimize speed))
                                    (declare (author "God"))
                                    (setf *universe* (big-bang))
                                    (frob-light))
                                  :documentation t)
  => ((SETF *UNIVERSE* (BIG-BANG)) (FROB-LIGHT))
  => ((DECLARE (OPTIMIZE SPEED)) (DECLARE (AUTHOR "God")))
  => "Also sprach Zarathustra.."

Exceptional Situations

Signals an error of type SIMPLE-ERROR if body-and-decls contains more than one documentation string, and documentation is true.

See Also

parse-declarations

Notes

The symbol PARSE-BODY is not exported from the library Parse-Declarations to avoid a name conflict with the Alexandria library. However, this symbol is guaranteed to be part of Parse-Declarations, and can hence be referenced directly, or imported explicitly, if users don't want to use Alexandria.


Previous: parse-body, Up: API

parse-declarations

Syntax

— Function: parse-declarations declarations-or-specifiers &optional compilation-env &key nostrip

⇒ declaration-env

Arguments and Values

declarations-or-specifiers
Either a list of declarations, or a list of declaration specifiers.
compilation-env
An environment object.
nostrip
A boolean.
declaration-env
A declaration-env

Description

The function parse-declarations returns a declaration-env that describes the declaration specifiers in declarations-or-specifiers. This declaration-env can be used to manipulate the declaration specifiers in various high-level ways, and finally to construct actual specifiers again.

If nostrip is true, declarations-or-specifiers should be a list of declaration specifiers rather than declarations.

Exceptional Situations

Signals an error of type TYPE-ERROR if the (implicit) specifiers in declarations-or-specifiers are not valid declaration specifiers.

See Also

analyze-declaration-specifier
build-declarations
declaration-env
filter-declaration-env
map-declaration-env
merge-declaration-envs
parse-body

Notes

The set of understood declaration specifiers can be extended by adding methods to the generic functions analyze-declaration-specifier and build-declaration-specifier.


Next: , Previous: API, Up: Top

3 Examples

DO

As first example, we provide an implementation of DO. Notice that CLHS 3.3.4 specifies that the step-forms, end-test-form, and result-forms of a DO expression must be evaluated in the scope of the local declarations given.

Thus:

  (defmacro do ((&rest bindings) (end-test-form &body result-forms) &body decls-and-body
                 &environment macro-env)
    (let ((loop-tag (gensym "DO-LOOP+")))
      (multiple-value-bind (statements decls)
          (parse-declarations::parse-body decls-and-body :documentation nil)
        `(prog ,(loop for binding in bindings
                      collect (destructuring-bind (var &optional init step) binding
                                (declare (ignore step))
                                `(,var ,init)))
            ,@(build-declarations 'declare (parse-declarations decls macro-env))
            ,loop-tag
            (when ,end-test-form (return (progn ,@result-forms)))
            ,@statements
            (psetq ,@(loop for binding in bindings
                           appending (destructuring-bind (var &optional init step) binding
                                       (declare (ignore init))
                                       (when step `(,var ,step)))))
            (go ,loop-tag)))))

In this example, all we had to do is to split a &BODY argument up into the real body forms and the declarations, because, conceptually, we only had to splice in a few new forms before the actual body forms. The declarations as such aren't touched.

Notice that the expression ,@(build-declarations 'declare (parse-declarations decls macro-env)) could (and should!) have been written much easier as a simple ,@decls. We wanted this to be a first and very simple introduction to the basic operators of the Parse-Declarations library, though.

LET*

Next we implement LET* which is a more interesting example as it's an example of a non-trivial binding construct. A naive implementor would just go off making a LET* form expand into nested LET forms. That would, however, be broken with respect to declarations which must be nested alongside the bindings they affect.

With the :affecting parameter of filter-declaration-env, we can easily extract only those declarations which affect a given binding. Thus:

  (defmacro let* (bindings &body body &environment macro-env)
    (flet ((normalize-binding (binding)
             (cond ((symbolp binding)    `(,binding nil))
                   ((null (cdr binding)) `(,(car binding) nil))
                   (t binding))))
      (multiple-value-bind (real-body decls) (parse-declarations::parse-body body :documentation nil)
        (let ((decl-env (parse-declarations decls macro-env)))
          (check-declaration-env decl-env :unknown-allowed nil :warn-only t)
          (labels ((generate-nested-lets (bindings &optional used-binding-names)
                     (if (null bindings)
                         `(locally
                              ,@(build-declarations 'declare
                                  (filter-declaration-env decl-env :include :free)
                                  (filter-declaration-env decl-env :include :bound
                                                          :not-affecting used-binding-names))
                            ,@real-body)
                         (destructuring-bind ((var value) . more-bindings) bindings
                           `(let ((,var ,value))
                              ,@(build-declarations 'declare
                                  (filter-declaration-env decl-env :affecting `(,var)))
                              ,(generate-nested-lets more-bindings (cons var used-binding-names)))))))
            (generate-nested-lets (mapcar #'normalize-binding bindings)))))))

We warn about unknown declaration specifiers, as we don't know which binding such a specifier affects. Hence, we can't know where it is supposed to be located. We could move these into the base case—which may sometimes be exactly the right decision—, but we feel that a user looking at the macroexpansion might be tricked thinking that the right thing is done even though it isn't.

For the base case, we splice in all free declaration specifiers and all bound declaration specifiers which affect bindings not established by the LET* form. These, plus the ignored unknown declaration specifiers and the bound declaration specifiers affecting bindings established by the LET* form, constitute all possible specifiers. So we haven't forgot any.

OPTIMIZE-DECLARATION-ENV

As last example, we show how to change the treatment of standard declaration specifiers.

We want to write a function which takes a list of optimize qualities and which adapts a given declaration-env to these qualities. Existing qualities in that declaration-env are changed, and if no qualities are given, the qualities are added. I.e.:

  (defun optimize-declaration-env (declaration-env qualities &aux result-env)
    (flet ((ensure-car (thing) (if (consp thing) (car thing) thing)))
      (setq result-env
            (map-declaration-env
             #'(lambda (id args ctx)   ; CTX of OPTIMIZE are the qualities.
                 (if (eq id 'optimize)
                     (values id args `(,@qualities ,@(set-difference ctx qualities :key #'ensure-car)))
                     (values id args ctx)))
             declaration-env))
      (if (declaration-env.policy result-env)
          result-env
          (merge-declaration-envs result-env (parse-declarations `((optimize ,@qualities)) nil
                                                                 :nostrip t)))))


Next: , Previous: Examples, Up: Top

Appendix A glossary

bound declaration specifier
A declaration specifier that with certainty affects a binding. Cf. CLHS bound declarations.


declaration arguments
A list of data interpreted according to rules specific to the declaration identifier the arguments belong to.


declaration
See CLHS declaration.


declaration identifier
The first element of a declaration specifier. This is either a symbol, or a a cons representing a compound type specifier. The difference from CLHS declaration identifier is that we explicitly allow any symbol, and conses. (Cf. CLHS 3.3.3.1)


declaration specifier
A list whose CAR is a declaration identifier, and whose CDR are the declaration arguments. See CLHS 3.3.2, CLHS 3.3.3.1, and CLHS declaration specifier.


free declaration specifier
A declaration specifier that does not affect a binding with certainty. Cf. CLHS free declarations.


normalized declaration identifier
A declaration identifier which analyze-declaration-specifier returns as first value.


normalized binding name
A binding name which is a symbol for a binding in the variable namespace, and a list whose first element is the symbol FUNCTION and the second element is the function name for a binding in the function namespace.


standard declaration specifier
A declaration specifier with one of the symbols in Fig 3-9 in CLHS 3.3.3 as declaration identifier.


unknown declaration specifier
A declaration specifier which is not explicitly known by analyze-declaration-specifier and which hence invokes its default method.


Previous: Glossary, Up: Top

Index