6.6.6 Advanced macros

Prev Up Next Page 84 of 800 Search internet

During compilation, lgc(1) invokes user defined functions in four cases: when macro expanding, when verifying, when rendering, and when unpacking. All four are Turing complete. As an example, you can define arbitrarily complex rendering and not just rendering by substitution as was done for x ++ y and lambda x dot y. You can also define arbitrarily complex macros and not just substitution macros like macro define lambda x dot y as \ x . y end define. As an example, << 1 ,, 2 ,, 3 >> macro expands to 1 :: 2 :: 3 :: true and similarly for arbitrarily long lists, and that requires more than simple substitution.

An example of a non-trivial macro

You may skip the following without loss of continuity.

Advanced macro expansion, verification, rendering, and unpacking is treated elsewhere, but as an appetizer, consider this definition of a macro for debugging macros:

01   ""P mdebug
02   ""R base
03   ""R lgc
04   ""D 0
05   mdebug ( " , " )
06   mdebug1 ( " )
07   ""B
08   page ( ""N , ""C )
09   title "A macro debug tool"
10   bib "@MISC{dummy}"
11   main text "
12   \begin{statements}
13   \item "[[ Macro define mdebug ( u , v ) as
14   \ x . mdebug1 ( x ) end define ]]"
15   \item "[[ eager define mdebug1 ( x ) as newline
16   let << t ,, s ,, c >> = x in newline
17   let << true ,, u ,, v >> = t in newline
18   let b _ { v } = quote v end quote :: v maptag  in newline
19   let b _ { c } = quote c end quote :: c maptag in newline
20   let b = << b _ { v } ,, b _ { c } >> in newline
21   println ( "Hello 1" ) .then. newline
22   eval ( u , b , c ) untag .then. newline
23   println ( "Hello 2" ) .then. newline
24   stateexpand ( v , s , c ) end define ]]"
25   \item "[[ etst mdebug ( println ( !"Now expanding " ::
26   tree2vt ( v , c ) ) , << 1 ,, 2 >> ) ; 1 :: 2 :: <<>> end test ]]"
27   \end{statements}
28   \nocite{dummy}\color{white}\bibliography{./page}
29   "
30   appendix "
31   \begin{statements}
32   \item Nothing here
33   \end{statements}
34   "
35   end page

The source text above defines a construct mdebug ( x , y ) which evaluates x, discards the result, and then macro expands y. Thus, x is evaluated during macro expansion.

Lines 25-26 use the construct. The second argument of mdebug reads << 1 ,, 2 >> which macro expands to 1 :: 2 :: <<>>. During macro expansion, however, mdebug has the side effect that a println statement is executed.

The argument of println is constructed from the string 'Now expanding' and the return value from tree2vt ( v , c ). Inside the first argument of mdebug, v is bound to the second argument of mdebug and c is bound to the 'cache' of the mdebug page. That cache contains, among other, all definitions on the mdebug page and all its transitively referenced pages. The tree2vt function converts the term v into the lgs representation of v using the name definitions in c.

The mdebug macro is defined in Lines 13-14. When the macro engine encounters an instance of mdebug, it applies the right hand side of the macro definition of mdebug to a structure x which contains everything a macro expander needs. For the mdebug macro, this leads to invocation of mdebug1 ( x ).

In Line 16 the mdebug1 function destructures x into a term t, a 'macro state' s, and a cache c. The term t is the term to be macro expanded and has mdebug as principal operator. The macro state indicates, among other, how subterms should be macro expanded. The cache c is the cache of the page on which the instance of mdebug occurs.

In Line 17 mdebug1 destructures the term t into the subterms u and v which are the first and second subterm, respectively, of the term to be macro expanded. Element number zero of t is a structure which represents the principal operator. Since we know it is mdebug we ignore it by inserting the constant true at that position.

In Line 22, mdebug1 computes the value of u and then discards the value. Thus, it only makes sense to do the evaluation if it has side effects such as a call to println. The computation is done by the eval function which takes three arguments: the term u to be evaluated, a list b of bindings from variables to values, and a cache c.

The eval function is eager but is capable of evaluating arbitrary lazy functions. To do so, it returns the result in maptagged form. Maptagged values are not evaluated before they are forced. The mdebug1 function uses untag to force evaluation of the return value from eval.

The list of variable bindings is constructed in Line 18-20. First, a binding from the variable v to the value of v is constructed. Then a binding from the variable c to the value of c. And finally the bindings are assembled into a list b of bindings.

The first binding is put in an indexed variable named b _ { c }. Indexed variables can be indexed by arbitrary terms. Two indexed variables are identical if the indexes are identical. As an example, b _ { 2 + 2 } and b _ { 4 } are distinct variables because 2 + 2 and 4 are not identical. They are just equal, but not identical as terms.

Since eval is capable of working with arbitrary values, not just eager ones, the values included in binding lists must be maptagged. The value assigned to b _ { c } is the pair quote v end quote :: v maptag where quote v end quote is a value which represent the term v and v maptag is the value of v with a maptag on top.

In Line 24, mdebug1 macro expands v as specified in the macro state s. The macro state s typically specifies default macro expansion, but there are situations where one may need to macro expand some subterm in a special way.

If you store the source above to mdebug.lgs and does

   > lgc mdebug.lgs

then the output will look like this:

   Reading   file:mdebug.lgs
   Fetching  base
   Codifying base
   Fetching  lgc
   Codifying lgc
   1st reading
   2nd reading
   Hello 1
   Now expanding << 1 ,, 2 >>
   Hello 2

   The page is correct

   Dumping to cache
   User rendering

During first reading, lgc sees the definition of mdebug for the first time. That definition has effect from the second reading. Thus, you see output from mdebug during second but not during first reading. Had there been a third reading then you would also have seen output from mdebug during the third reading.

Note that there is no output from mdebug during verification. Macro expansion only occurs during the readings. At the time of verification, the macro expansion has finished.

Prev Up Next Page 84 of 800 Search logiweb.eu

Copyright © 2010 Klaus Grue, GRD-2010-01-05