10 Macros
define constant
, address that need.Throughout this chapter, italic font and small caps are used to indicate references to the formal grammar given in Appendix A, "BNF."
Parsing a stream of characters into tokens, according to the lexical grammar in Appendix A, "BNF."
Parsing a stream of tokens into a program, according to the phrase grammar in Appendix A, "BNF."
Macro expansion, which translates the program to a core language.
Definition processing, which recognizes special and built-in definitions and builds a compile-time model of the static structure of the program.
Optimization, which rewrites the program for improved performance.
Code generation, which translates the program to executable form.
Portions of a program can be macro calls. Macro expansion replaces a macro call with another construct, which can itself be a macro call or contain macro calls. This expansion process repeats until there are no macro calls remaining in the program, thus macros have no space or speed cost at run time. Of course, expanding macros affects the speed and space cost of compilation.
A macro definition describes both the syntax of a macro call and the process for creating a new construct to replace the macro call. Typically the new construct contains portions of the old one, which can be regarded as arguments to the macro. A macro definition consists of a sequence of rewrite rules. The left-hand side of each rule is a pattern that matches a macro call. The right-hand side is a template for the expansion of a matching call. Pattern variables appearing in the left-hand side act as names for macro arguments. Pattern variables appearing in the right-hand side substitute arguments into the expansion. Macro arguments can be constrained to match specified elements of the Dylan grammar. Auxiliary rule sets enhance the rewrite rule notation with named subrules.
Some implementations and a future version of the Dylan language specification might allow macro expansions to be produced by compile-time computation using the full Dylan language and an object-oriented representation for programs. Such a "procedural macro" facility is not part of Dylan at this time.
The input to, and output from, macro expansion is a fragment, which is a sequence of elementary fragments. An elementary fragment is one of the following:
(
, )
, [
, ]
, {
, }
, #(
, and #[
are not allowed. Core reserved words (except otherwise
), begin-words, and function-words are not allowed unless quoted with backslash.
()
, []
, or {}
) enclosing a fragment.
The second and third phases of compilation (parsing and macro expansion) are interleaved, not sequential. The parsing phase of the compiler parses a macro call just enough to find its end. See definition-macro-call, statement, function-macro-call, body-fragment, list-fragment, and basic-fragment in Appendix A, "BNF." This process of parsing a macro call also parses any macro calls nested inside it. The result is a macro call fragment.
This loose grammar for macro calls gives users a lot of flexibility to choose the grammar that their macros will accept. For example, the grammar of macro calls doesn't care whether a bracketed fragment will be interpreted as an argument list, a parameter list, a set of for
clauses, or a module import list.
The compiler can compute the expansion of a macro call fragment immediately, or delay computing it until it is needed. When the compiler computes the expansion of a macro call fragment, it obeys the macro's definition. Constraints on pattern variables can cause reparsing of portions of the macro call.
A constituent, operand, or leaf that is a macro call expands the macro some time before the definition processing and optimization phases. The compiler brackets the expansion in begin
... end
, using the standard binding of begin
in the Dylan module, and then reparses it as a statement. This reparsing may discover more macro calls. A parse error while reparsing a macro expansion could indicate an invalid macro definition or an incorrect call to the macro that was not detected during pattern matching. Once the cycle of macro expansion and reparsing has been completed, no tokens, bracketed fragments, or macro call fragments remain and the entire source record has become one parsed fragment.
This begin
... end
bracketing ensures that the expansion of a macro call will not be broken apart by operator precedence rules when the macro call is a subexpression. Similarly it ensures that the scopes of local declarations introduced by a macro will not extend outside that macro expansion when the macro call is a statement in a body.
The fragment produced by parsing a macro call, which is the input to macro expansion, looks like this:
The parser recognizes parsed fragments as well as raw tokens. The nonterminals expression, definition, and local-declaration in the phrase grammar accept parsed fragments of the same kind. The nonterminal constant accepts parsed expression fragments that are constants. The nonterminals ordinary-name and name accept parsed expression fragments that are named value references. The nonterminal operand accepts all parsed expression fragments. The nonterminals macro, definition-macro-call, statement, and function-macro-call accept macro call fragments.
Generated with Harlequin WebMaker