Understanding how macro expansion works

395 views
Skip to first unread message

Nicolas Petton

unread,
Dec 1, 2020, 7:12:00 AM12/1/20
to tiddl...@googlegroups.com, tiddly...@googlegroups.com
Hi all,

I haven't touched TW in quite some time. Yesterday I wanted to get back
to a project I started 6 months ago, and was quickly stuck when using
macros, realizing that my assumptions on how macro expansion works were
wrong.

I would expect macros to be expanded recursively, before any parsing is
done on the string. It seems to be mostly the case, but I do not
understand the following.

If I define & use macros as

\define foo() 42

\define bar() <<foo>>

<span><<bar>></span>

The expansion works as I'm expecting it to, and the output is

<span>42</span>

However, if I use it as below:

\define foo() 42

\define bar() <<foo>>

<span class=<<bar>>></span>

Then the expansion yields

<span class="<<foo>>">test</span>


As if all of the sudden macro expansion wasn't recursive anymore.
I think I'm misunderstanding something :-)

Cheers,
Nico
signature.asc

Saq Imtiaz

unread,
Dec 1, 2020, 7:29:16 AM12/1/20
to TiddlyWiki
If I define & use macros as

\define foo() 42

\define bar() <<foo>>

<span><<bar>></span>

The expansion works as I'm expecting it to, and the output is

<span>42</span>

Macros only do text substitution and then transclude the resultant string. So the macro <<bar>> returns <<foo>> but the entire string is then wikified as it is part of the wikitext of a tiddler, that is it is run through the parser and the rendered result ends up in the DOM. Under the covers, macros are variables.
 
\define foo() 42

\define bar() <<foo>>

<span class=<<bar>>></span>

Then the expansion yields

<span class="<<foo>>">test</span> 

When you use variable widget attributes (or indirect attributes via references), the value of the variable is fetched/transcluded and that literal value is assigned to the attribute. There is no further wikify/rendering step.
 
The difference is in context. In the first example the macro is used in wikitext and is wikified/rendered after transclusion. In the second the macro is assigned to an attribute, and the value is transcluded but there is no wikify/render step.

You can use the wikify widget to render a macro and assign the rendered value to a variable for further use. This should be a last resort as it comes with a refresh performance penalty and can almost always be avoided with alternative code patterns.

Note that the rules of assigning attributes to HTML elements and widgets are the same, as under the covers all HTML elements are handled by an <$element> widget.

Hope this helps.
Saq

Eric Shulman

unread,
Dec 1, 2020, 7:37:19 AM12/1/20
to TiddlyWiki
On Tuesday, December 1, 2020 at 4:12:00 AM UTC-8 Nicolas Petton wrote:
I would expect macros to be expanded recursively, before any parsing is
done on the string. It seems to be mostly the case, but I do not
understand the following.

If I define & use macros as
\define foo() 42
\define bar() <<foo>>
<span><<bar>></span>

The expansion works as I'm expecting it to, and the output is
<span>42</span>

However, if I use it as below:
\define foo() 42
\define bar() <<foo>>
<span class=<<bar>>></span>


Then the expansion yields
<span class="<<foo>>">test</span>

As if all of the sudden macro expansion wasn't recursive anymore.
I think I'm misunderstanding something :-)

Macros only do 2 things:
1) replace instances of $param$ with the values passed into the macro
2) replace instances of $(param)$ with the values of variables defined outside the macro

Other than those two actions, the macro contents are simply "returned" for further processing in the calling context.  It is this context which determines what happens next.  If the macro occurs within wikitext (your first example), then the macro call is replaced by its output and parsing continues with the first character of the replaced content.  If that output contains a macro call (as in your example), then that macro is processed, and its contents are returned for further processing.  Thus, multiple layers of macro calls are *iteratively* expanded.  If a macro call invokes itself, then this is would be referred to as *recursive* expansion.

In contrast... if the macro call is used as a parameter value in a widget, then the macro call is replaced by its output as before.  However... all further processing is left to be handled by the widget itself.   This also applies when the macro output is used as a parameter value in HTML syntax, giving the results that you see in your second example.

If you want a widget or HTML parameter macro call to be iteratively expanded, then you can do this explicitly, by using the <$wikify> widget.  Thus:
<$wikify name="myparam" text=<<bar>>>
<span class=<<myparam>>></span>
</$wikify>

-e

Nicolas Petton

unread,
Dec 1, 2020, 7:54:34 AM12/1/20
to Saq Imtiaz, TiddlyWiki
Saq Imtiaz <saq.i...@gmail.com> writes:

Hi Saq,

> When you use variable widget attributes (or indirect attributes via
> references), the value of the variable is fetched/transcluded and that
> literal value is assigned to the attribute. There is no further
> wikify/rendering step.
>
> The difference is in context. In the first example the macro is used in
> wikitext and is wikified/rendered after transclusion. In the second the
> macro is assigned to an attribute, and the value is transcluded but there
> is no wikify/render step.

That explains it, thanks.

Being used to other macro systems, I remember having a hard time last
time I used macros in TiddlyWiki (that was quite some time ago now).

I think it would be good to document how macro expansion works in
different contexts (or maybe it is documented but I missed it?).

Thanks again,
Nico
signature.asc

Nicolas Petton

unread,
Dec 1, 2020, 7:56:56 AM12/1/20
to Eric Shulman, TiddlyWiki
Eric Shulman <elsd...@gmail.com> writes:

Hi Eric,

> Other than those two actions, the macro contents are simply "returned" for
> further processing in the calling context. It is this context which
> determines what happens next. If the macro occurs within wikitext (your
> first example), then the macro call is replaced by its output and parsing
> continues with the first character of the replaced content. If that output
> contains a macro call (as in your example), then that macro is processed,
> and its contents are returned for further processing.

Thanks for the explanations, it makes sense now.

Cheers,
Nico
signature.asc

Saq Imtiaz

unread,
Dec 1, 2020, 8:01:46 AM12/1/20
to TiddlyWiki
Hi Nico,

I think it would be good to document how macro expansion works in
different contexts (or maybe it is documented but I missed it?).

I think the information is there but spread out in several places, like "macros in wikitext" and "widget attributes". So it can be difficult to grasp unless you read the documentation thoroughly. 

This topic comes up often enough that it deserves a more accessible explanation where we pull together all the details in one place, so that it can serve as a one-stop reference for macros/variables.

Regards,
Saq

Mohammad

unread,
Dec 1, 2020, 9:10:46 AM12/1/20
to TiddlyWiki
Hi Nico
Welcome back. 

While you got a very good explanations from experts, I may suggest to have https://kookma.github.io/TW-Scripts/ as collection of solutions always may give you some direction.

Off topic:

I know your Project  Manager Manager plugin and more specific the Notebook theme and palette (https://nicolas.petton.fr/tw/project-manager.html) have a lot of fans! You may put the code on GitHub lets user send their issue and idea there.

I use Notebook on mobile and its amazing! Thank you Nico.

Cheers
Mohammad

Nicolas Petton

unread,
Dec 2, 2020, 3:29:51 AM12/2/20
to Mohammad, TiddlyWiki
Mohammad <mohammad...@gmail.com> writes:

Hi Mohammad,

> Welcome back.

Thanks :)

> Off topic:
>
> I know your Project Manager plugin and more specific the Notebook
> theme and palette (https://nicolas.petton.fr/tw/project-manager.html) have
> a lot of fans! You may put the code on GitHub lets user send their issue
> and idea there.

Yes, that's exactly what I'm planning to do. I resumed work on both
projects.

> I use Notebook on mobile and its amazing! Thank you Nico.

Glad you like it!

Cheers,
Nico
signature.asc

Mohammad

unread,
Dec 2, 2020, 4:14:50 AM12/2/20
to TiddlyWiki
Thanks Nico!

Sylvain Naudin

unread,
Dec 16, 2020, 12:18:01 PM12/16/20
to TiddlyWiki
Good evening,

I take the liberty of picking up this thread, I think you might be able to help me.
In my quest to make the colour of the favicon in SVG format customizable, I'm coming up with this simple macro for now:

\define favi(color)
<svg xmlns="http://www.w3.org/2000/svg" width="144.798" height="87.014" viewBox="0 0 38.311 23.022">
  <path d="M19.41.004L2.814.88A2.97 2.97 0 00.008 3.634l-.001.023a2.646 2.646 0 002.45 2.828l3.958.284A1.438 1.438 0 017.75 8.187l.138 11.97a2.866 2.866 0 002.66 2.824l.01.001a2.45 2.45 0 002.625-2.456L13.12 8.56a3.124 3.124 0 012.585-3.093l1.328-.233a1.419 1.419 0 011.65 1.2l2.03 14.453a1.401 1.401 0 002.396.777l3.76-3.905a1.694 1.694 0 012.595.186l3.31 4.606a1.13 1.13 0 002.027-.447l3.48-18.22a1.67 1.67 0 00-1.12-1.899l-1.274-.417a2.066 2.066 0 00-2.704 1.815l-.473 6.598a.882.882 0 01-1.621.414l-1.146-1.78a1.39 1.39 0 00-2.217-.159l-1.908 2.198a.97.97 0 01-1.673-.396l-2.13-8.34A2.551 2.551 0 0019.409.005z" fill="$color$" />
</svg>
\end

<$button class="" tooltip="Color Favicon" aria-label='Color Favicon'>Add color to $:/favicon.ico
        <$action-setfield $tiddler="$:/favicon.ico" text=<<favi "black">> />
</$button>

Now I've tried to make the colour configurable, ideally one that could be chosen beforehand (I already have the code to add this in the palette).
So I would like to refer to a variable that would look for the value in the index $:/n0d1/palettes/n0d1##favicon-color

Have try different methods, but don't succeed (wikify ?, var, other ?).

Thanks,
Sylvain

Jeremy Ruston

unread,
Dec 16, 2020, 12:39:17 PM12/16/20
to TiddlyWiki Group
Hi Sylvain

Just set the type field to indicate that it is an SVG image:

<$button class="" tooltip="Color Favicon" aria-label='Color Favicon'>Add color to $:/favicon.ico
        <$action-setfield $tiddler="$:/favicon.ico" text=<<favi "black">> type="image/svg+xml"/>
</$button>

Best wishes

Jeremy

Sylvain Naudin

unread,
Dec 16, 2020, 2:24:08 PM12/16/20
to TiddlyWiki
Hi Jeremy,

Thanks but I don't worried about type field (because I use my existing $:/favicon.ico tiddler, but you're right it's better to be add this).

My concern is about to set color (in the example black) as variable, and not hard coded in the macro.

Do you have an idea ?

Thanks :)

TW Tones

unread,
Dec 16, 2020, 6:50:35 PM12/16/20
to TiddlyWiki
The main issue maybe is a $:/favicon which is loaded perhaps before any other logic is occurring.

Are you trying to get the favicon icon to convey information according to its color? Nice Idea.

Tones

Sylvain Naudin

unread,
Dec 17, 2020, 5:50:31 AM12/17/20
to TiddlyWiki
Hi Tones,

It's not a problem for me, look at my attempt.
I just write the content when user want to change color via config tiddler, so it's not an issue about loaded.

For the moment with the simple macro I can do it, but it's hard coded.
I would like to make the choice of colour settings.
Ideally with a single button or drop-down list, but doing it in 2 steps can satisfy me too.

Capture d’écran 2020-12-17 à 11.45.30.png

Do you see my goal ? I don't know how to use wikify output of $:/n0d1/palettes/n0d1##favicon-color and inject it in my macro.
I feel like it's all silly, but it has to be me :D

Regards,
Sylvain

Sylvain Naudin

unread,
Dec 21, 2020, 5:52:18 PM12/21/20
to TiddlyWiki
FYI, I used a diversion and thought that maybe I could concatenate the SVG code by cutting it in 3 :
- the beginning of SVG code
- the part with the hexadecimal code
- At the end of the SVG tag


I can now set color with picker :D (see at https://silvyn.github.io/tw-minstyle/ under button "Plus d'options" in sidebar )

1. <$edit-text tiddler="$:/favicon.ico/temp2" field="text" type="color" tag="input"/>
2. <$button class="ms-outline-info">OK
   <$wikify name="out" text="""<$list filter="[tag[$:/favicon.ico]]"><$text text={{!!text}}/></$list>""">
      <$action-setfield $tiddler="$:/favicon.ico" type="image/svg+xml" text=<<out>> />
   </$wikify>
</$button>

I still wonder if it wouldn't be possible to do this with a single button with several actions inside, and above all cleaner.

Sorry for the hijacking of this discussion, I'm done ;)

Mohammad Rahmani

unread,
Dec 21, 2020, 11:34:28 PM12/21/20
to tiddl...@googlegroups.com
Hi Sylvain,

Really smart solution! Bravo!

One usecase is the day/night palette switch, to adjust the color of favicon and logo as you switch from a day palette to night palette!
It can even be used for all toolbar/page control svgs to be affected on palette change!

I tagged this thread to document your solution in TW-Scripts. But if you had time please add explanation in  https://silvyn.github.io/tw-minstyle.

Thank you
Mohammad


--
You received this message because you are subscribed to the Google Groups "TiddlyWiki" group.
To unsubscribe from this group and stop receiving emails from it, send an email to tiddlywiki+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/tiddlywiki/e314131c-62cb-47e0-9cd5-d89db686e41an%40googlegroups.com.

Mohammad Rahmani

unread,
Dec 21, 2020, 11:49:54 PM12/21/20
to tiddl...@googlegroups.com
Sylvain,
In https://silvyn.github.io/tw-minstyle/ create a new tiddler with the below content

<style>
.myclass  {
background-image: url(<$macrocall $name="datauri" title="$:/favicon.ico" $output="text/plain"/>);
background-repeat: no-repeat;
height: 400px;
background-position:calc(100% - 20px) calc(100% - 20px);
border:1px dotted grey;
font-size:4em;
line-height: 4em;
font-weight:300;
text-align:center;
}
</style>

<div class="myclass">
Hello Sylvain!
</div>

1. Save tiddler and see how it looks!
2. Change the color of favicon from sidebar
3. close and reopen the tiddler of step 1

Works great! It seems datauri macro does not trigger a refresh when its content is changed, but closing and reopening the tiddler shows the effect.
THis way you can use SVG images as element backgrounds  (e.g tiddler background) , create watermarks or put logos!

Best wishes
Mohammad


On Tue, Dec 22, 2020 at 2:22 AM Sylvain Naudin <sil...@gmail.com> wrote:
Reply all
Reply to author
Forward
0 new messages