WML_TUTORIAL(7)

πŸ“¦
version 2.32.0 Β· 3 versions
, in debian bookworm, debian trixie, ubuntu jammy, ubuntu noble, ubuntu questing

WML_TUTORIAL(7) πŸ’–

okay bestie, welcome to the EN Tools WML Tutorial and I promise this is gonna be way more fun than it sounds. It's the WML Tutorial β€” Understanding WML step-by-step, and we're doing it in tiny little lessons, one vibe at a time. We start easy and trivial and end up hard and complex, so buckle up πŸŽ€


✨ NAME ✨

WML Tutorial - Understanding WML step-by-step β€” that's the whole thing, that's the name, iconic.


🌸 DESCRIPTION 🌸

So this tutorial is a step-by-step introduction to all the features of WML, and it's chopped up into tiny lessons so your brain doesn't explode πŸ’… Each lesson shows you one particular aspect or feature of WML, and the lessons go from easy and trivial all the way to hard and complex. We're leveling up, no cap πŸ“ˆ


πŸ¦‹ IMPLICIT MARKUP PROCESSING πŸ¦‹

⭐ LESSON: Plain Data Throughput ⭐

first lesson, easiest W, let's go: WML is 95% of the time transparent to its input, meaning we can pass through literally any data without corrupting it. slay.

Input:

 1| foo
 2| <bar>
 3| quux

Output:

 1| foo
 2| <bar>
 3| quux

Why? Because per default there are neither definitions for the symbols "foo" or "quux", nor a defined HTML tag named "<bar>". And because there are no unnecessary whitespaces in this example, the input can't be stripped in any case. periodt πŸ’…


πŸŽ€ LESSON: Protected Markup Code πŸŽ€

Sometimes situations happen where some of your markup code or page contents conflicts with WML because of overlapping tagnames, etc. And here WML interprets some stuff you actually don't want to be interpreted. rude of it honestly 😭

Input:

 1| foo: foo.c
 2|    $(CC) -o foo foo.c

Output:

 1| foo: foo.c
 2|     -o foo foo.c

See that? The "$(CC)" got expanded to an empty string because IPP uses the same syntax for variable interpolation like make. To avoid this, just surround the critical part with the WML-internal "<protect>" container tag. problem solved bestie 🫢

Input:

 1| foo: foo.c
 2|    <protect>$(CC)</protect> -o foo foo.c

Output:

 1| foo: foo.c
 2|    $(CC) -o foo foo.c

πŸ’œ LESSON: Stripped-Down Markup Code πŸ’œ

now let's try an example with unnecessary whitespaces. But be careful β€” unnecessary here means they can be stripped as long as the resulting Webpage displays the same in a Webbrowser as the original. That's the vibe check for whitespace.

Input:

 1| <body>
 2|
 3| <img src = "file.gif"    alt="  test  " >
 4| <pre>
 5|
 6|     Preformatted          Text
 7| </pre>
 8|     Not    Preformatted   Text
 9| </body>

Output:

 1| <body>
 2| <img src="file.gif" alt=" test ">
 4| <pre>
 5|
 6|     Preformatted          Text
 7| </pre>
 8|     Not Preformatted Text
 9| </body>

Here we see that line 2 is completely removed because empty lines have no effect in HTML. The whitespaces between the attribute "src" and its value are removed too. And all double whitespaces get replaced by a single whitespace character. But not inside preformatted areas, bestie, never there πŸ™ˆ


πŸ”₯ LESSON: Fixed And Adjusted Markup Code πŸ”₯

now assume we have an image.gif file containing a GIF image with a size of 500x400 pixels and the following input page:

 1| <body>
 2| <center>
 3| <font color=336699>Headline:</font><br>
 4| <img src="image.gif">
 5| </center>
 6| </body>

Even though this is valid HTML code, WML can enhance it to make it more portable, speed up its rendering in the Webbrowser and make Lynx users more happy. So WML recognizes the "<img>" tag and automatically adds missing information and replaces obsolete tags with up-to-date variants. it's giving glow-up ✨

 1| <body>
 2| <div align=center>
 3| <font color="#336699">Headline:</font><br>
 4| <img src="image.gif" alt="" width="500" height="400">
 5| </div>
 6| </body>

As you can see, WML did three things:

  1. replaced the proprietary "<center>" tag with the HTML 3.2 pedant "<div align=center>",
  2. fixed the "color" attribute of "<font>",
  3. and added the missing "alt" and "width"/"height" attributes.

ate that πŸ’…


🌈 STRUCTURING THE MARKUP CODE 🌈

πŸ“ LESSON: Using Include Files πŸ“

One of the most useful features of WML is the ability to move commonly used stuff into include files which can be selectively read in at later steps. obsessed.

Assume we have an include file bar.wml...

 1| bar
 2| The value of bar is: $(bar:-unknown)

...and the following input file:

 1| foo
 3| #include 'bar.wml' bar="FooBar"
 2| #include 'bar.wml'
 5| quux

Then the output is:

 1| foo
 2| bar
 3| The value of bar is: Foobar
 4| bar
 5| The value of bar is: unknown
 6| quux

As you can see, the "#include" directive is replaced by the contents of the corresponding file. And this included contents can contain variables which are interpolated when they are defined, inclusive default values. iykyk 🧚

There's also a way to create simple constructs similar to an if-then-else just by using variable interpolation:

 1| The value of bar is $(bar:+set)$(bar:*unset).

Here the "$(bar:+set)$(bar:*unset)" construct emulates the following semantics:

 if (isdefined(bar))
     expandto("set")
 if (not isdefined(bar))
     expandto("unset")

πŸ¦„ LESSON: Concatenating Lines πŸ¦„

Although HTML usually does not care about whitespaces and newlines, sometimes it's very frustrating to create preformatted areas or write own tags (see later) without the ability to spread the code over more than one line while it should actually be one single line. For this, a lot of languages use a line concatenation/continuation character "\", as does WML. iconic behavior honestly.

Input:

 1| foo\
 2|    bar \
 3| quux

Output:

 1| foobar quux

The line concatenation strips whitespaces from the begin of concatenated lines but preserves whitespaces at the end of them, i.e. you can use leading whitespaces for structuring your input nicely but still use appended whitespaces for real ones. best of both worlds, we love a flexible queen πŸ‘‘


πŸ’« LESSON: Diverting To Locations πŸ’«

One of the most powerful features of WML is the ability to divert data at any point to locations defined at any other point. yeah, it goes both ways, main character energy fr fr.

Input:

 1| {#BAR#:this is:##}
 2| foo
 3| {#BAR#}
 4| quux
 5| {#BAR#: bar:##}
 6| foobar
 7| {#BAR#}

Output:

 1| foo
 2| this is bar
 3| quux
 4| foobar
 5| this is bar

Here in line 3 the location "BAR" is already dumped, but filled at lines 1 and 5. And as you can see a location can be dumped at any point and even more than once. And you can accumulate the contents for a location by subsequent fills (line 1 and 5). This works because in WML first all locations are filled in a first pass and then dumped in a second pass. two-pass queen πŸ’…

With the use of the high-level tags from wml::std::tags we could also write the example above in a little bit more human readable way:

 1| #use wml::std::tags
 2| <divert BAR>this is</divert>
 3| foo
 4| <dump BAR>
 5| quux
 6| <divert BAR> bar</divert>
 7| foobar
 8| <dump BAR>

🌸 LESSON: Defining Output Slices 🌸

Often one needs more than one output file. Usually although 90% of the contents is the same, one needs a way to select the remaining 10%. WML's approach here is to write these 10% directly in the input file but separate the variants by defining slices which later can be used to create the different output files. lowkey genius.

 1| <html>
 2| <head>
 3| <title>[EN:Titleline:][DE:Titelzeile:]</title>
 4| </head>
 5| <body>
 6| <h1>[EN:Headerline:][DE:Ueberschrift:]</h1>
 7| </body>
 8| </html>

Now assume the above page is in file index.wml, then the command

 $ wml -o UNDEF+EN:index.html.en \
       -o UNDEF+DE:index.html.de index.wml

generates the output file "index.html.en" containing the union of all undefined areas and the slices "EN"...

 1| <html>
 2| <head>
 3| <title>Titleline</title>
 4| </head>
 5| <body>
 6| <h1>Headerline</h1>
 7| </body>
 8| </html>

...and the output file "index.html.de" containing the union of all undefined areas and the slices "DE":

 1| <html>
 2| <head>
 3| <title>Titelzeile</title>
 4| </head>
 5| <body>
 6| <h1>Ueberschrift</h1>
 7| </body>
 8| </html>

✨ FORMATTING ✨


πŸŽ€ LESSON: Area Substitution πŸŽ€

okay so WML has this thing called area substitution and it's lowkey iconic πŸ’…. the way it works: you say where the area begins and where it ends, and in the middle you drop in some Perl substitution and translation commands. that's it. that's the whole vibe.

Input:

 1| foo
 2| {: [[s/foo/bar/g]] [[s/quux/foobar/g]] [[tr/[a-z]/[A-Z]/]]
 3| this is foo and quux.
 4| :}
 5| quux

Output:

 1| foo
 2| THIS IS BAR AND FOOBAR.
 3| quux

ngl that alone looks kinda pointless bestie 😭 so let's go further and show you an actual example using the <isolatin> and <url> container tags from wml::fmt::isolatin and wml::fmt::url β€” because those are literally programmed this exact way. periodt.

Input:

 1| #use wml::fmt::isolatin
 2| #use wml::fmt::url
 3| <isolatin><url>
 4| Some umlauts `XXXXXX' and
 5| a hyperlink http://foo.bar.com/
 6| </url></isolatin>

Output:

 1| Some umlauts `&ouml;&auml;&uuml;&Ouml;&Auml;&szlig;' and
 2| a hyperlink <a href="http://foo.bar.com/">http://foo.bar.com/</a>

🌸 LESSON: Text Formatting 🌸

real talk: HTML sucks the moment you want to write more than one paragraph of actual text 😭. so WML comes through and gives you nice ways to format an area of input using other (externally available) markup language processors. here's an example that uses two embedded areas β€” the first one written in Plain Old Document (POD) format, and the second one written in Simple Document Format (SDF). two icons in one document, we love to see it 🫢

Input:

  1| #use wml::fmt::pod
  2| #use wml::fmt::sdf
  3| <html>
  4| <pod notypo>
  5| =head1 Headline1
  6|
  7| Foo
  8|
  9| =head2 Headline1.1
 10|
 11| Bar
 12| </pod>
 13|
 14| <sdf notypo>
 15| H1: Headline1
 16|
 17| Foo
 18|
 19| H2: Headline 1.1
 20|
 21| Bar
 22|   * Baz
 23|     - Foobar
 24|     - Quux
 25|   * Foo
 26| </sdf>
 27| </html>

Output:

  1| <html>
  2| <P>
  3| <H1><A NAME="Headline1">Headline1
  4| </A></H1>
  5| Foo
  6| <P>
  7| <H2><A NAME="Headline1_1">Headline1.1
  8| </A></H2>
  9| Bar
 10| <P>
 11| <H1><A NAME="Headline1">1. Headline1</A></H1>
 12| <P>Foo</P>
 13| <H2><A NAME="Headline 1.1">1.1. Headline 1.1</A></H2>
 14| <P>Bar</P>
 15| <UL>
 16| <LI>Baz<UL>
 17| <LI>Foobar
 18| <LI>Quux</UL>
 19| <LI>Foo</UL>
 20| </html>

πŸ’œ LESSON: Table Formatting πŸ’œ

another spot where HTML markup code is just unproductive and ugly is when you have to write <table> structures πŸ™ˆ. so WML hands you two new container tags to make your life easier. bestie behavior fr fr.

🌈 <grid>

the whole goal of this container tag is to let you specify tables the same way you already have them in your head β€” i.e. as a 2-dimensional grid. so: you specify a grid-layout, then you fill its cells. easy. and on top of that, <grid> gives you a cute little feature for specifying the cell alignments. obsessed.

Input:

 1| #use wml::std::grid
 2| <grid layout=2x3 align=lr valign=tbb border=1>
 3|   <cell>Header 1</cell> <cell>Header 2</cell>
 4|   <cell>Cell-A</cell>   <cell>Cell-B</cell>
 5|   <cell>Cell-C</cell>   <cell>Cell-D</cell>
 6| </grid>

Output:

 1| <table border="1" cellspacing="0" cellpadding="0">
 2|   <tr>
 3|     <td align=left valign=top>Header 1</td>
 4|     <td align=right valign=top>Header 2</td>
 5|   </tr>
 6|   <tr>
 7|     <td align=left valign=bottom>Cell-A</td>
 8|     <td align=right valign=bottom>Cell-B</td>
 9|   </tr>
10|   <tr>
11|     <td align=left valign=bottom>Cell-C</td>
12|     <td align=right valign=bottom>Cell-D</td>
13|   </tr>
14| </table>

πŸ¦‹ <xtable>

this is the extended <table> container tag β€” its syntax is provided by the external freetable program. its goal: give you a compact syntax for specifying a table. and again, same example, because we're consistent like that πŸ’…

Input:

 1| #use wml::fmt::xtable
 2| <xtable border=1>
 3|   (*, 1) align=left
 4|   (*, 2) align=right
 5|   (1, *) valign=top
 6|   (2|3, *) valign=bottom
 7|   (1,1)
 8|   Header 1
 9|   (1,2)
10|   Header 2
11|   (2,1)
12|   Cell-A
13|   (2,2)
14|   Cell-B
15|   (3,1)
16|   Cell-C
17|   (3,2)
18|   Cell-D
19| </xtable>

Output:

 1| <table border="1">
 2|   <tr valign=top>
 3|     <td align=left>Header 1</td>
 4|     <td align=right>Header 2</td>
 5|   </tr>
 6|   <tr>
 7|     <td align=left valign=bottom>Cell-A</td>
 8|     <td align=right valign=bottom>Cell-B</td>
 9|   </tr>
10|   <tr>
11|     <td align=left valign=bottom>Cell-C</td>
12|     <td align=right valign=bottom>Cell-D</td>
13|   </tr>
14| </table>

πŸ‘‘ DEFINITION OF OWN HTML TAGS πŸ‘‘

πŸŽ€ LESSON: Simple Tags And Container Tags πŸŽ€

okay now it's time. we're about to level up our markup language by defining new custom HTML tags. there are two types of HTML tags. let's meet them 🫢

✨ Simple Tags

as an example, let's define a <me> tag that expands to my name abbreviation. iconic, obviously.

Input:

 1| <define-tag me whitespace=delete>
 2| rse@engelschall.com
 3| </define-tag>
 4|
 5| This is <me>.

Output:

 1| This is rse@engelschall.com.

πŸ’– Container Tags

as an example, let's define a <red> tag which changes its body text color to red. main character energy only.

Input:

 1| <define-tag red endtag=required whitespace=delete>
 2| <font color="#cc3333">%body</font>
 3| </define-tag>
 4|
 5| This is <red>very important</red>.

Output:

 1| This is <font color="#cc3333">very important</font>.

πŸ’… LESSON: Tags With Attributes πŸ’…

because tags without attributes are not very flexible (fr fr, they're giving nothing), there's also a way to define tags that can use them. slay.

Input:

 1| <define-tag me whitespace=delete>
 2| <if   "%0" ""            "rse@engelschall.com">
 3| <ifeq "%0" "engelschall" "rse@engelschall.com">
 4| <ifeq "%0" "netsw"       "rse@netsw.org">
 5| <ifeq "%0" "apache"      "rse@apache.org">
 6| <ifeq "%0" "freebsd"     "rse@freebsd.org">
 7| <ifeq "%0" "sdm"         "rse@sdm.de">
 8| </define-tag>
 9|
10| This is <me> and <me apache>.

Output:

 1| This is rse@engelschall.com and rse@apache.org.

there's also a variant where you use attributes of type name=value. so cute.

Input:

 2| <define-tag me whitespace=delete>
 3| <preserve at>
 4| <set-var %attributes>
 5| <if   "<get-var at>" ""            "rse@engelschall.com">
 6| <ifeq "<get-var at>" "engelschall" "rse@engelschall.com">
 7| <ifeq "<get-var at>" "netsw"       "rse@netsw.org">
 8| <ifeq "<get-var at>" "apache"      "rse@apache.org">
 9| <ifeq "<get-var at>" "freebsd"     "rse@freebsd.org">
10| <ifeq "<get-var at>" "sdm"         "rse@sdm.de">
11| <restore at>
12| </define-tag>
13|
14| This is <me> and <me at=apache>.

Output:

 1| This is rse@engelschall.com and rse@apache.org.

πŸ”₯ LESSON: Overwriting Existing HTML Tags πŸ”₯

WML also gives you a way to overwrite existing HTML tags β€” meaning you can define a custom tag with the same name as an already-known HTML tag, and then use the original HTML tag inside it. yes bestie, tag-ception. it's giving genius 🧚

Input:

 1| <define-tag br whitespace=delete>
 2| <br*><br*>
 4| </define-tag>
 5|
 6| Some Text<br>
 7| Some more Text

Output:

 1| Some Text<br><br>
 2| Some more Text

⭐ LESSON: Programming Tags In Perl ⭐

one of the essential features in WML is that you can embed Perl code at any point, just marked with <: and :> delimiters. and you can combine that with tag definitions by programming tags in Perl. this is where it gets chef's kiss πŸ’‹

Input:

 1| #use wml::std::tags
 2| <define-tag me whitespace=delete>
 3| <preserve at>
 4| <set-var %attributes>
 5| <:{
 6|     my $at = qq/<get-var at>/;
 7|     my $addr;
 8|     $addr = "rse\@engelschall.com" if $at eq '';
 9|     $addr = "rse\@engelschall.com" if $at eq 'engelschall';
10|     $addr = "rse\@netsw.org"       if $at eq 'netsw';
11|     $addr = "rse\@apache.org"      if $at eq 'apache';
12|     $addr = "rse\@freebsd.org"     if $at eq 'freebsd';
13|     $addr = "rse\@sdm.de"          if $at eq 'sdm';
14|     print $addr;
15| }:>
16| <restore at>
17| </define-tag>
18|
19| This is <me> and <me at=apache>.

Output:

 1| This is rse@engelschall.com and rse@apache.org.

✨ ADVANCED FEATURES ✨


πŸŽ€ LESSON: Using Templates

okay so bestie, remember how we learned to divert data to a location? πŸ’– Because WML automatically closes any still-open diversions at EndOfFile, we can totally use that feature to build templates. Iconic behaviour, honestly.

Say we have this template defined in the file template.wml:

 1| #   the template itself
 2| <html>
 3| <head>
 4| <title>{#SUBJECT_LOC#}</title>
 5| </head>
 6| <body>
 7| <h1>{#SUBJECT_LOC#}</h1>
 8| <blockquote>
 9| {#BODY#}
10| </blockquote>
11| </body>
12| </html>
13|
14| #   way to insert the subject
15| <define-tag subject>
16| {#SUBJECT_LOC#:%0:##}
17| </define-tag>
18|
19| #   per default we are in body
20| {#BODY#:

Then the input:

 1| #include 'template.wml'
 2|
 3| <subject "Foo, Bar and Quux">
 4|
 5| This is about Foo, Bar and Quux...

And the output, slay:

 1| <html>
 2| <head>
 3| <title>Foo, Bar and Quux</title>
 4| </head>
 5| <body>
 6| <h1>Foo, Bar and Quux</h1>
 7| <blockquote>
 8| This is about Foo, Bar and Quux...
 9| </blockquote>
10| </body>
11| </html>

And you can even nest more than one template, because the diversion mechanism in WML accepts location dumps and location fills at any point β€” even inside other location fills. πŸ¦‹ nesting like it's a set of those little Russian dolls, periodt.


🌈 LESSON: Creating Multi-Lingual Pages

The core languages of WML don't provide a dedicated facility for making multi-lingual pages β€” like, one or more output pages generated from a single input source, each one carrying the same page info but in a different human language. BUT 🫢 WML provides variants through "slicing" (Pass 9), and human languages are just a special case of general variants. So wml::std::lang exists, and it gives you specialized multi-lingual support tags which get mapped to slices, which you can then use to create all the different output files. it's giving "one source, many vibes".

Let's take an example:

 1| #!wml -o (ALL-LANG_*)+LANG_EN:index.en.html \
 2|       -o (ALL-LANG_*)+LANG_DE:index.de.html
 3|
 4| #use wml::std::page
 5| #use wml::std::lang
 6|
 7| <lang:new id=en short>
 8| <lang:new id=de short>
 9|
10| <page>
11|
12| <h1><en: Welcome><de: Willkommen>!</h1>
13|
14| <a href="<lang:star: index2.*.html>">Index 2</a>
15|
16| <lang:area>
17| (en)This is a test page
18| (de)Dies ist eine Testseite
19| </lang:area>

After processing passes 1 to 8 (wml -p1-8), WML internally generates this:

 1| <html>
 2| <head>
 3| </head>
 4| <body bgcolor="#ffffff" text="#000000" link="#333399" alink="#9999ff" vlink="#000066">
 5| <h1>[LANG_EN:Welcome:][LANG_DE:Willkommen:]!</h1>
 6| <a href="[LANG_EN:index2.en.html:][LANG_DE:index2.de.html:]">Index 2</a>
 7| [LANG_EN:This is a test page
 8| :][LANG_DE:Dies ist eine Testseite:]
 9| </body>
10| </html>

And then after processing pass 9 with the initial WML magic cookie line ("#!wml -o..."), these two files get generated πŸ’…:

index.en.html:

 1| <html>
 2| <head>
 3| </head>
 4| <body bgcolor="#ffffff" text="#000000" link="#333399" alink="#9999ff" vlink="#000066">
 5| <h1>Welcome!</h1>
 6| <a href="index2.en.html">Index 2</a>
 7| This is a test page
 8|
 9| </body>
10| </html>

index.de.html:

 1| <html>
 2| <head>
 3| </head>
 4| <body bgcolor="#ffffff" text="#000000" link="#333399" alink="#9999ff" vlink="#000066">
 5| <h1>Willkommen!</h1>
 6| <a href="index2.de.html">Index 2</a>
 7| Dies ist eine Testseite
 8| </body>
 9| </html>

And those two pages can then be served either by a content negotiation feature of the webserver, or by explicit references. ✨


πŸ’œ MORE INFORMATION

okay so now you've seen the various core languages of WML in action, bestie!! πŸŽ‰ For the gory details of what each language gives you, either read the all-in-one WML Introduction in wml_intro(7), or step through the particular manpages of the core languages. Start here with the frontend wml(1). πŸ‘‘

And you can also step through the set of available standard include files that WML ships with. Start with the top-level include file wml::all(3). πŸ“


🌸 SEE ALSO


and that's a wrap on the whole tutorial, my love πŸ’– this page came from EN Tools, dated 2020-11-29, and it's the WML_TUTORIAL(7) manpage. go forth and build something iconic with it, no cap πŸ§šβœ¨πŸ‘‘

πŸ“¦ where this page comes from