You have probably used them already: container macros and the
<$content> tag. And perhaps you have also noticed,
that in some examples I have
used somewhat different constructions than what the documentation says you
should. Yes, there's a reason apart from elitism ;-)
Basically, as the documentation says, you declare a macro with the
/CLOSE modifier, and access the stuff you wrote between the start
and end tag with <$content>. Like this:
<$macro SIMON-SAYS /CLOSE> Simon says: <$content> </$macro>
Obviously, this will prepend everything you enclose in these tags with "Simon says:". But that may be too unflexible for you. Let's say one of your customers likes the pages you do but doesn't like this bloke from the bible, he'd rather have Muhammad tell him what to do. So to avoid having to rewrite your pages, you define a new macro:
<$macro SIMON-SAYS /CLOSE>
<$if COND=(defined SIMONSUCKS)>
Muhammad says:
<$else>
Simon says:
</$if>
<$content>
</$macro>
Testing:
<SIMON-SAYS>use HSC!</SIMON-SAYS><BR> <$define SIMONSUCKS:bool> <* type doesn't matter *> <SIMON-SAYS>use HSC!</SIMON-SAYS>
Simon says: use HSC!
Muhammad says: use HSC!
They agree! :-)
Now what about using both versions in one document? If you have Simon on top, no
problem. But once the variable is defined, Simon is gone until the end of the
document. SIMONSUCKS indeed...
How about HSC's local variables? If you had another container macro, you could
define SIMONSUCKS locally and it would disappear as soon as HSC
sees this macro's end tag. Let's try it:
<$macro NOSIMON /CLOSE> <$define SIMONSUCKS:bool> <$content> </$macro> <NOSIMON> <SIMON-SAYS>use HSC!</SIMON-SAYS> </NOSIMON> <SIMON-SAYS>use HSC!</SIMON-SAYS>
Simon says: use HSC!
Simon says: use HSC!
What's that? It says "Simon" twice, while the first time it should have said
"Muhammad"—that's what we defined the <NOSIMON> macro for.
Apparently, SIMONSUX is not defined in an
enclosed container macro, so local variables can not be used!
Of course there is a solution, or this article would be fairly pointless. If you have read the loops tutorial, you probably guessed what to do:
<$macro NOSIMON /CLOSE> <$define SIMONSUCKS:bool> <(HSC.Content)> <*---Look-here--- *> </$macro> <NOSIMON> <SIMON-SAYS>use HSC!</SIMON-SAYS> </NOSIMON> <SIMON-SAYS>use HSC!</SIMON-SAYS>
Muhammad says: use HSC!
Simon says: use HSC!
There you are!
Frankly I'm not sure whether Thomas thought of these implications when he designed HSC, because in his macros this mechanism is never used. However, it is the key to loads of programming tricks, including loops, semiautomatic layout and other weird things probably never meant to be in HSC =^>
The reason for the difference between <$content> and
<(HSC.Content)> is the order of expansion:
<$content> is first expanded, then its result is
inserted in the enclosing macro.
<(HSC.Content)> is first inserted verbatim, i.e. including all
unexpanded macro calls, and is then expanded together with its
container in the current context
Everybody confused? Fine. Took me a while to figure it out, too ;)
If you take the last example using <$content> and expand the macro
calls "by hand", you get this:
<$macro SIMON-SAYS /CLOSE>
<$if COND=(defined SIMONSUCKS)>
Muhammad says:
<$else>
Simon says:
</$if>
<$content>
</$macro>
<$macro NOSIMON /CLOSE>
<$define SIMONSUCKS:bool>
<$content>
</$macro>
<NOSIMON>
<SIMON-SAYS>use HSC!</SIMON-SAYS>
</NOSIMON>SIMON-SAYS expanded
<NOSIMON> Simon says: use HSC! </NOSIMON>
<$define SIMONSUCKS:bool> Simon says: use HSC!
Obviously the variable definition doesn't have any effect on the already expanded macro call below it. Now take the other construction using <(HSC.Content)> and expand it:
<$macro SIMON-SAYS /CLOSE>
<$if COND=(defined SIMONSUCKS)>
Muhammad says:
<$else>
Simon says:
</$if>
<$content>
</$macro>
<$macro NOSIMON /CLOSE>
<$define SIMONSUCKS:bool>
<(HSC.Content)>
</$macro>
<NOSIMON>
<SIMON-SAYS>use HSC!</SIMON-SAYS>
</NOSIMON>SIMON-SAYS inserted in outer macro
<NOSIMON>
<$if COND=(defined SIMONSUCKS)>
Muhammad says:
<$else>
Simon says:
</$if>
use HSC!
</NOSIMON><$define SIMONSUCKS:bool>
<$if COND=(defined SIMONSUCKS)>
Muhammad says:
<$else>
Simon says:
</$if>
use HSC!That's the whole trick. The final evaluation of the <$if> block
will throw away the Simon part and we get the result we want.
Writing your own macros I suppose you'll generally want the second form
using <(HSC.Content)>, because it works just the same for simple
cases as decribed in "Defining your own logical styles" (although it may be
slower; I don't have a CPU that would make this noticeable any more) but
allows for a lot of tricks beyond that, most importantly macros that can access
their "parent's" local variables and even modify them. This mechanism will be
used ad nauseam in the part on semi-automatic layout, once I get
around to writing it :) However, using this may make debugging your HSC code
more difficult, because you can get very long "backtraces" for every error,
indicating every nested level of expansion1.
NONESTERR parameter!Last change: 21-Feb-2006, 06:43