00
|
Previous
|
Up
|
Next
Building software building softwareBuilding software building software Episode 1: Macros - source to source transformation. Next Thursday, March 6th, in the 4'th floor large conference room of building 6, from 12:00 to 13:30, i plan to give the first episode of a series of talks on building software that builds other software out of common household ingredients. This episode is on macros and will cover such topics as
For slides see: http://openmap.bbn.com/~kanderso/building/macro |
01
|
Previous
|
Up
|
Next
Software building software
Learn the ancient ways, Learn the secrets of the experts, Use them in our programs. Head toward the future, open implementation, open compilers... |
02
|
Previous
|
Up
|
Next
Lisp is a building material
Building materials
|
03
|
Previous
|
Up
|
Next
A little Lisp in Java
|
04
|
Previous
|
Up
|
Next
Software = data
can be represented by a data structure constructed by the Lisp code:
So one piece of code generates another piece of code. |
05
|
Previous
|
Up
|
Next
Defining a macro extends the syntax of the language |
06
|
Previous
|
Up
|
Next
ok-items in Java
|
07
|
Previous
|
Up
|
Next
Macroexpansion, the detailsdefmacro defines a macro-function that might look
something like:
When the Lisp evaluator[1] sees a form beginnng with my-push,
it runs its
macro-function to get a replacement form, which is then reevaluated.
Thus the part of the Lisp evaluator that deals with macroexpansion might look
like:
The variable env is the runtime environment that form is to be evaluted
in.
[1] Lisp is a compiled language, |
08
|
Previous
|
Up
|
Next
Backquote simplifies construction of expessionsTo construct an expression, we must use list construction primitives:
like
Alternatively, we can use the backquote macro character "`":
The backquoted expression provides a template for the expression you want
to generate.
|
09
|
Previous
|
Up
|
Next
Basic idea of backquote
is equivalent to:
Specifically:
|
10
|
Previous
|
Up
|
Next
Use hygienic macros
Here, the expansion of my-push uses the local definition of
cons because cons (and setf)
occurs free in the macro expansion.
To avoid this problem, you are not allowed to redefine any symbol in the common-lisp package. Scheme has developed several hygienic macro approaches [scheme refrences].
Extra credit: What if the |
11
|
Previous
|
Up
|
Next
Avoid unintentional variable capture
In the third example, the outer binding of The inner binding is not apparent to a reader. |
12
|
Previous
|
Up
|
Next
Solution: Use unique variable names
Each binding is unique.
Expands to:
|
13
|
Previous
|
Up
|
Next
However, variable capture can be useful
Sample use:
Use variable names that are distinctive, like |
14
|
Previous
|
Up
|
Next
Avoid redundant computation
The first version calls
The macro once-only can be helpful here.
|
15
|
Previous
|
Up
|
Next
Don't use a macro when you can use a function...The problems with
|
16
|
Previous
|
Up
|
Next
Compute what you can at compile time: GarnetGarnet is an object oriented graphics system [REF?]. In Garnet, an object has a 8 element hash table of slots. A slot's value might be computed as:
The
Unfortunately, while Garnet uses an approach like this, in user code
the macro is always exanded in a context where |
17
|
Previous
|
Up
|
Next
Compute what you can at compile time: keywordsThe overhead of keyword argument processing can be removed by using a macro with keywords that calls a normal function.
The disadvantage is that Compiler macros can be used to overcome this problem. CLIM uses this approach. |
18
|
Previous
|
Up
|
Next
Specialize functions on constant (static) arguments.When a function is controlled by a constant argument, a specialized version of the function can be written. Example: mem?
(defun mem?-rt (item items)
(if (consp items)
(if (eql item (car items)) T
(mem?-rt item (cdr items)))
NIL))
(defmacro mem? (item items)
(if (constant? items)
(c-mem? item (constant-value items))
`(mem?-rt ,item ,items)))
(defun constant? (x)
(if (consp x) (eq (car x) 'quote)
t))
(defun constant-value (x)
(if (consp x) (second x) x))
(defun c-mem? (item items)
(if (consp items)
`(if (eql ,item ',(car items)) T
,(c-mem? item (cdr items)))
NIL))
? (macroexpand '(mem? x '(#\space #\tab #\newline)))
(IF (EQL X '#\Space) T (IF (EQL X '#\Tab) T (IF (EQL X '#\Newline) T NIL)))
T
? (macroexpand '(mem? x (cdr y)))
(MEM?-RT X (CDR Y))
T
|
19
|
Previous
|
Up
|
Next
Leave only small footprintsAs with inlined functions, each macroexpansion takes up space in your application. Just as inlined functions only make sense if they are small, keep a macro's code footprint small. Expand a macro into a function call that does most of the work at load time. See slot-ref for a good example.
|
20
|
Previous
|
Up
|
Next
Writing macro writing macros - Comma comma comma quote comma commaMacro's that write macros can be tricky because they can require nesting of backquotes. Paul Graham has a nice way to construct such macros. Example: Abbreviations - some names in Lisp are long. Step 1: Write out an example of what the macro should do:
Step 2: Replace constants with variables:
Step 3: Make a template out of the defmacro [single comma form].
Step 4: Simplify by removing the let binding [optional].
|
21
|
Previous
|
Up
|
Next
Guy Steele's wholesale clubDouble backquotes lead to comma expressions that can be recognized wholesale [CLTL2 p. 530].
|
22
|
Previous
|
Up
|
Next
Common Lisp is 10% macros (MCL 3.1)
AMP defines 906 macros. |
23
|
Previous
|
Up
|
Next
Usage of macros (MCL 3.1)
|
24
|
Previous
|
Up
|
Next
Build your own language: CollectingCollecting is a common idiom:
A
|
25
|
Previous
|
Up
|
Next
Collecting implementationThus we want the macro to look something like:
Issue: Should a user have access to
Removing the innermost
|
26
|
Previous
|
Up
|
Next
Build your own language: DefineScheme has a defining form,
You can also define "curried" functions [See next episode]. So if the uncurried version looks like:
The curried version would look like:
Here's the macro:
This version also subsumes Using such a defining macro lets us control such things as:
|
27
|
Previous
|
Up
|
Next
Building a data abstractionA simple record facity like that in Elements of Programming Languages.
SLOTS is a list of slot name symbols. Accessor macros
The predicate Each variant has a unique, unforgable variant-ID. Each variant instance is a vector with the variant-id as the initial element.
The function make-name is needed at compile time because
it is used by define-variant.
It could be flet'd inside the macro to avoid this.
|
28
|
Previous
|
Up
|
Next
Example variant applicationElements of a Lisp program might be represented by the following variants:
Thus, the expression
could be represented as:
|
29
|
Previous
|
Up
|
Next
Running this representationSomething like this could execute (run) this representation:
The
|
30
|
Previous
|
Up
|
Next
The variant-case macro
Macroexpansion looks like:
|
31
|
Previous
|
Up
|
Next
With-* macro example: HTML generationA This code printed these slides[cl-http]:
|
32
|
Previous
|
Up
|
Next
Transforming an interpreter into a compilerWhen you want a compiler, start with an interpreter.
|
33
|
Previous
|
Up
|
Next
Example: Regular expression matchingThe function Let's add UNIX "glob" style matching where ? matches any character * matches 0 or more characters. \ removes any special meaning from the following character. Here's some examples:
|
34
|
Previous
|
Up
|
Next
Converting a string into a pattern
The function
Basically, the string is converted into a list of characters except that the special characters #\* and #\? are converted into the symbols '* and '?. |
35
|
Previous
|
Up
|
Next
Version 1: a tail recursive interpreter
This structure is typical of an interpreter, a case dispatch controlled by a static parameter. [1]This is the only nontail recursive call to [2]This is a self call that can be replaced by a |
36
|
Previous
|
Up
|
Next
Version 2: Replace self call by loop
Replace each call to The pattern gets shorter each time so there is no infinite recursion. |
37
|
Previous
|
Up
|
Next
Version 3: A pattern compiler
This is not a macro, but a code generating function. It can be used in a macro or to generate a compiled function. We have transformed an interpeter into a compiler. |
38
|
Previous
|
Up
|
Next
My-apropos Version 2
This version is about 50% faster. Extra Credit: The Extra Credit:Extend |
39
|
Previous
|
Up
|
Next
A language for lexical analysisLex is a well known lexical analyzer generator. It generates a tokenizer from a set of rules. Each rule has two parts, a pattern to be matched, and an action to be executed when the pattern is matched. My problems with Lex:
A solution: Write a tiny Lex, that even i can remember how to use. Advantages:
Disadvantages:
|
40
|
Previous
|
Up
|
Next
Henry Baker's Meta parserHenry G. Baker, Pragmatic Parsing in Common Lisp; or, putting defmacro on Steroids, Lisp Pointers, IV, 2, 1991, p. IV-2.3 - IV-2.15.
(match-it pattern) returns T if characters read using meta-read-char and
meta-peek-char match the pattern, Pattern language:
|
41
|
Previous
|
Up
|
Next
Example: Read numbers and identifiers separated by spaces23 Lines macroexpands into 70.
|
42
|
Previous
|
Up
|
Next
Auxilliary functions
|
43
|
Previous
|
Up
|
Next
The Meta Parser
|
44
|
Previous
|
Up
|
Next
Tiny lexical analyzersThe macros required took about as much space as the documentation. Here are the number of lines for 3 similar lexers on the same problem, and a Lex parser:
The Extra Credit: Write a macro that takes a list of tokens like '("<" "<=" ">" ">=" "<<" ">>" "+" "+=" ...) and produces a recognizer for them in this language. Hint: Since the language does not backtrack, generate the software so no backtracking is necessary. |
45
|
Previous
|
Up
|
Next
Why don't other languages have macros?
|
46
|
Previous
|
Up
|
Next
Aspects of macroexpansion
|
47
|
Previous
|
Up
|
Next
C macro examples: Exception handlingAn exception handling system needs:
syntax stmt throw {| $$exp::value ; |}
{if (simple_expression(value))
return(
`{if (exception_pointer == NULL)
error("No handler for %d", $value);
else longjump(exception_ptr, $value);});
else
return(
`{int the_value = $value;
if (exception_ptr == NULL)
error("No handler for %d", the_value);
else longjmp(exception_ptr, the_value);});}
syntax stmt catch
{| $$exp::tab { $$stmt::handler } { $$stmt::body } |}
{return(
`{int *old_exception_ptr = exception_ptr;
int jmp_buf[2], result;
result = setjump(jmp_buf);
if (result == 0)
{exception_ptr = jmp_buf; $body}
else {exception_ptr = old_exception_ptr;
if (result == $tag)
$handler else throw result;}});}
syntax stmt unwind_protect
{| { $$stmt::body } { $$stmt::cleanup } |}
{return(
`{int *old_exception_ptr = exception_ptr;
int jmp_buf[2];
int result = setjump(jmp_buf);
if (result = 0)
{exception_ptr = jmp_buf; $body}
exception_ptr = old_exception_ptr;
$cleanup;
if (result != 0) throw result;});}
|
48
|
Previous
|
Up
|
Next
Example usage
|
49
|
Previous
|
Up
|
Next
Additional topics and resourcesTopics
Resources
|
50
|
Previous
|
Up
|
Next
To sum up
|
51
|
Previous
|
Up
|
Next
Next time: Closures - crystalized software
|