.\" Automatically generated by Pod::Man 4.10 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "HOOLA 7" .TH HOOLA 7 "2018-12-25" "EN Tools" "EN Tools" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" WML Macros \- Writing powerful WML macros .SH "DESCRIPTION" .IX Header "DESCRIPTION" This tutorial is a guide for writing macros in \s-1WML.\s0 It should help beginners to write their first templates, but also give useful hints to write tricky macros. To take best benefit of this document, it is highly recommended to read documentation of individual passes first. .PP Following examples are compiled with .PP .Vb 1 \& wml \-q \-p123 test.wml .Ve .PP Most of them could be passed through \fImp4h\fR only, but the line below is more generic. .SH "INTRODUCTION" .IX Header "INTRODUCTION" .SS "Definitions" .IX Subsection "Definitions" These definitions are those used in this document, they may differ from those of the W3C because i do not want to enter into deep details. .IP "\(bu" 2 A \fItag\fR is a portion of text enclosed between bracket angles, like .Sp .Vb 4 \& \& \& \& .Ve .IP "\(bu" 2 A \fIstart\fR tag is a tag which begins an \fIelement\fR (see below). It consists of a left angle bracket, followed by the element name, optional \&\fIattributes\fR (see below), and a right angle bracket. All these are start tags: .Sp .Vb 3 \& \& \& .Ve .IP "\(bu" 2 An \fIend\fR tag is a tag which ends an \fIelement\fR (see below). It consists of a left angle bracket, a slash, the element name, and a right angle bracket, like in .Sp .Vb 2 \& \& .Ve .Sp This tag cannot contain attributes. .IP "\(bu" 2 An \fIelement\fR is an elementary unit of the document. It mainly consists of pair of start and end tags, like in .Sp .Vb 1 \& Click here .Ve .IP "\(bu" 2 The \fIbody\fR of an element is the portion of text contained between the start and the end tags. In the example above, there is one element, which name is \f(CW\*(C`a\*(C'\fR, and its body is "\f(CW\*(C`Click here\*(C'\fR". .IP "\(bu" 2 \&\fIAttributes\fR are parameters to make elements more flexible. They must be put in the start tag. An element may have any number of attributes, which are separated by one or more spaces, tabulations or newlines. Each element may define which attributes are mandatory and which are optional. .Sp .Vb 2 \& Logo .Ve .Sp The \f(CW\*(C`img\*(C'\fR element has 3 attributes .IP "\(bu" 2 A \fIsimple tag\fR is an element without end tag. .IP "\(bu" 2 A \fIcomplex tag\fR is an element with start and end tags. .SS "First contact" .IX Subsection "First contact" Basically all macro definitions are performed with the \&\f(CW\*(C`\*(C'\fR. Here is a trivial example: .PP Input: .PP .Vb 4 \& 1| \& 2| bar \& 3| \& 4| .Ve .PP Output: .PP .Vb 4 \& 1| \& 2| \& 3| bar \& 4| .Ve .PP Whereas trivial this example shows some interesting points: .IP "\(bu" 2 Newlines are preserved, there is the same number of lines on input and output, but we will discuss about whitespaces in detail below. .IP "\(bu" 2 Tag names are case insensitive. .SS "About Simple Tags" .IX Subsection "About Simple Tags" In \s-1HTML\s0 simple tags are an element without end tag, e.g. .PP .Vb 1 \&
.Ve .PP But \s-1XML\s0 specifies that simple tags must be written with one of these 2 forms: .PP .Vb 2 \&

\&
.Ve .PP i.e. either as a complex tag, without body, or by adding a trailing slash to the start tag. The first one will not work with \s-1WML,\s0 and also may confuse \s-1HTML\s0 browsers, and so should be avoided. You have to choose to write this trailing slash or not, \s-1WML\s0 works with both forms. .PP In this document, i will now always write simple tags with this trailing slash, to conform to the new \s-1XHTML\s0 standard. This is my preferred writing of input text, but one may still continue without this trailing slash. You decide to which syntax you want to conform to. .PP On the other hand, \s-1HTML\s0 browsers may be confused by \&\s-1XHTML\s0 syntax, so output text does not contain this trailing slash. This seems contradictory, but with this approach our input files are ready to be processed by future \s-1XML\s0 tools, and we only have to run \s-1WML\s0 with adequate flags to produce \s-1XHTML\s0 compliant pages. .SH "DEFINING NEW TAGS" .IX Header "DEFINING NEW TAGS" Each time a known element is found in input text, it is removed and its replacement text is put here. After that, this replacement text is scanned in case it contains other macros. .PP All user macros are defined with the \f(CW\*(C`define\-tag\*(C'\fR element. Its first attribute is the macro name which is defined, and its body function is the replacement text which is inserted in lieu of this macro. .PP Let us begin with a simple example: .PP Input: .PP .Vb 2 \& 1| http://www.engelschall.com/sw/wml/ \& 2| .Ve .PP Output: .PP .Vb 2 \& 1| \& 2| http://www.engelschall.com/sw/wml/ .Ve .PP Defining a complex tag is no more difficult, just add an \&\f(CW\*(C`endtag=required\*(C'\fR attribute. .PP Input: .PP .Vb 2 \& 1| bar \& 2| baz .Ve .PP Output: .PP .Vb 2 \& 1| \& 2| bar .Ve .SS "Special Text" .IX Subsection "Special Text" Some strings have a special meaning when found in replacement text, to allow full customization of macros: .ie n .IP "%0 %1 ..." 2 .el .IP "\f(CW%0\fR \f(CW%1\fR ..." 2 .IX Item "%0 %1 ..." Attributes: \f(CW%0\fR is the first attribute, \f(CW%1\fR the second, and so on. .ie n .IP "%name" 2 .el .IP "\f(CW%name\fR" 2 .IX Item "%name" Macro name .ie n .IP "%attributes" 2 .el .IP "\f(CW%attributes\fR" 2 .IX Item "%attributes" Space-separated list of all attributes .ie n .IP "%body" 2 .el .IP "\f(CW%body\fR" 2 .IX Item "%body" Macro body (for complex tags only) .IP "%#" 2 Number of arguments .IP "%%" 2 A percent sign .PP Input: .PP .Vb 12 \& 1| \& 2| Macro name: %name \& 3| Number of arguments: %# \& 4| First argument: %0 \& 5| Second argument: %1 \& 6| All arguments: %attributes \& 7| Body macro: %body \& 8| \& 9| \& 10| And the body \& 11| goes here. \& 12| .Ve .PP Output: .PP .Vb 12 \& 1| \& 2| \& 3| Macro name: foo \& 4| Number of arguments: 3 \& 5| First argument: Here \& 6| Second argument: are \& 7| All arguments: Here are attributes \& 8| Body macro: \& 9| And the body \& 10| goes here. \& 11| \& 12| .Ve .PP These special strings may also be altered by modifiers, which are a set of letters (one or more) put after the percent sign. These modifiers, and their actions, are: .IP "U (Unexpanded)" 2 .IX Item "U (Unexpanded)" Text is replaced, but not expanded (see section about expansion for details). .IP "A (Array)" 2 .IX Item "A (Array)" Lists are separated by newlines instead of spaces. This modifier makes sense with \f(CW%attributes\fR only. .Sp Input: .Sp .Vb 9 \& 1| \& 2| First argument: %A0 \& 3| All arguments: %Aattributes \& 4| Body macro: %Abody \& 5| \& 6| \& 7| And the body \& 8| goes here. \& 9| .Ve .Sp Output: .Sp .Vb 11 \& 1| \& 2| \& 3| First argument: Here \& 4| All arguments: Here \& 5| are \& 6| attributes \& 7| Body macro: \& 8| And the body \& 9| goes here. \& 10| \& 11| .Ve .PP Note that these sequences are replaced when macro is read, after what replacement text is scanned again. This is very important, because you should never write constructs like .PP .Vb 1 \& %body /> .Ve .PP Indded, \f(CW%body\fR is replaced \fIbefore\fR \f(CW\*(C`\*(C'\fR element is scanned, which may cause unpredictable results. A better solution is .PP .Vb 1 \& "%body" /> .Ve .PP but it will cause trouble when \f(CW%body\fR contains double quotes. For this reason, you should never use \f(CW\*(C`\*(C'\fR (and derivatives) tests when one of its arguments is a special sequence. Use instead .PP .Vb 3 \& > \& %body \& .Ve .SH "WHITESPACES" .IX Header "WHITESPACES" Previous examples show that expansion prints lots of unused newlines. There are some techniques to remove them. The first one is with pass 1, by putting a backslash at end of line, which will discard this end of line. .PP Input: .PP .Vb 4 \& 1| \e \& 2| bar\e \& 3| \e \& 4| .Ve .PP Output: .PP .Vb 1 \& 1| bar .Ve .PP Another solution is to specify \f(CW\*(C`whitespace=delete\*(C'\fR when defining macros, e.g. .PP .Vb 4 \& 1| \& 2| bar \& 3| \& 4| .Ve .PP Output: .PP .Vb 2 \& 1| \& 1| bar .Ve .PP The first line is caused by newline after \f(CW\*(C`
\*(C'\fR which is not discarded. .PP When this attribute is used, all trailing and leading whitespaces are removed, and also newlines outside of angle brackets. .SH "MACROS WITH ATTRIBUTES" .IX Header "MACROS WITH ATTRIBUTES" One nice feature of \s-1WML\s0 is its ability to deal with arbitrary attributes. There are many ways to define macros accepting attributes, we will discuss here the one used in all \s-1WML\s0 modules, and is so the standard way. .PP Attributes are stored in variables, because \s-1HTML\s0 syntax \f(CW\*(C`attribute=value\*(C'\fR is very closed to assignment to variables. In order to keep variables local, a mechanism of push/pop is used. Here is an example .PP Input: .PP .Vb 11 \& 1| \& 2| \& 3| \& 4| \& 5| "" \& 6| /> \& 7| \& 8| \& 9| \& 10| \& 11| .Ve .PP Output: .PP .Vb 2 \& 1| \& 2| http://www.w3.org/ .Ve .PP The \f(CW\*(C`\*(C'\fR tag pushes the variable passed in argument in top of a stack and clears this variable. So this variable is non-null only when it has been assigned via \f(CW\*(C`\*(C'\fR. The \f(CW\*(C` tag pops the value at top of the stack and sets the variable passed in argument to this value. .PP In \s-1HTML\s0 some attributes are valid without value. This attribute may be detected with .PP Input: .PP .Vb 10 \& 1| #use wml::std::info \& 2| \& 3| \& 4| \& 5| \& 6| \& 7| \& 8| " "" > \& 9| " ""> \& 10| \& 11| \& 12| \& 13| \& 14| \& 15| .Ve .PP Output: (only non-blank lines are printed) .PP .Vb 7 \& Test page 1 \& \& \& \& \& \& Test page 2 .Ve .SH "QUOTING AND GROUPING" .IX Header "QUOTING AND GROUPING" In \s-1HTML\s0 it is possible to specify attributes containing several words, by quoting them with single or double quotes. \s-1WML\s0 knows only double quotes. .PP .Vb 6 \& 1| \e \& 2| Number of arguments: %# \& 3| First argument: %0 \& 4| \e \& 5| \e \& 6| \e .Ve .PP Output: .PP .Vb 4 \& 1| Number of arguments: 3 \& 2| First argument: Here \& 3| Number of arguments: 2 \& 4| First argument: Here are .Ve .SH "EXPANSION" .IX Header "EXPANSION" In this section, all examples are processed with the command line .PP .Vb 1 \& wml \-W2,\-dat \-q \-p123 .Ve .PP and all output lines beginning with \f(CW\*(C`trace\*(C'\fR are generated by these debug flags. .PP This section is harder to understand, but one can work with \s-1WML\s0 without understanding it, because these notions are required in rare cases (mostly when writing macros for \s-1WML\s0 tutorials). .PP By default, macros are expanded when tags are scanned. .PP Input: .PP .Vb 3 \& 1| %attributes\e \& 2| baz\e \& 3| .Ve .PP Output: .PP .Vb 5 \& 1| trace: \-1\- \& 2| trace: \-1\- \& 3| trace: \-2\- \& 4| trace: \-1\- \& 5| name=baz .Ve .PP We see that the \f(CW\*(C`\*(C'\fR macro is processed first (digit between hyphens represent enesting level), and then \f(CW\*(C`\*(C'\fR. Indeed \s-1WML\s0 finds the \f(CW\*(C`foo\*(C'\fR name. As this is a macro name, attributes are searched for. When scanning attributes, it finds the \f(CW\*(C`\*(C'\fR. As this macro has no attribute, it is now replaced by its replacement text, after that scanning of \f(CW\*(C`\*(C'\fR attributes is finished. .PP Consider now .PP Input: .PP .Vb 3 \& 1| %attributes\e \& 2| baz\e \& 3| .Ve .PP Output: .PP .Vb 6 \& 1| trace: \-1\- \& 2| trace: \-1\- \& 3| trace: \-2\- \& 4| trace: \-1\- > \& 5| trace: \-1\- \& 6| name=baz .Ve .PP The \f(CW\*(C`attributes=verbatim\*(C'\fR attribute tells \s-1WML\s0 that when scanning this macro attributes, no expansion is performed. So the four first lines are now easy to understand. But after \f(CW\*(C`\*(C'\fR is expanded into .PP .Vb 1 \& name= .Ve .PP this text is scanned again and \f(CW\*(C`\*(C'\fR is expanded in turn. .PP The solution to forbid this expansion is to use the \f(CW\*(C`U\*(C'\fR modifier, explained in section \fBSpecial Text\fR. .PP Input: .PP .Vb 3 \& 1| %Uattributes\e \& 2| baz\e \& 3| .Ve .PP Output: .PP .Vb 5 \& 1| trace: \-1\- \& 2| trace: \-1\- \& 3| trace: \-2\- \& 4| trace: \-1\- > \& 5| name= .Ve .SH "MIXING MP4H AND EPERL" .IX Header "MIXING MP4H AND EPERL" After these preliminaries it is time to see how to mix \fImp4h\fR and \&\fIePerl\fR. The following section is a bit tricky, you may skip to section \fBHow to use these macros\fR to quickly learn which changes are needed. .SS "Nested ePerl macros do not work" .IX Subsection "Nested ePerl macros do not work" Consider this macro: .PP .Vb 1 \& <: print "attrs:%attributes"; :> .Ve .PP At first look, it behaves like .PP .Vb 1 \& attrs:%attributes .Ve .PP But what happens when these macros are nested? .PP Input: .PP .Vb 1 \& 1| /> .Ve .PP Output: .PP .Vb 1 \& 1| attrs:attrs:0 .Ve .PP It works fine! On the other hand, .PP Input: .PP .Vb 1 \& 1| /> .Ve .PP Output: .PP .Vb 11 \& 1| ePerl:Error: Perl parsing error (interpreter rc=255) \& 2| \& 3| \-\-\-\- Contents of STDERR channel: \-\-\-\-\-\-\-\-\- \& 4| Backslash found where operator expected at /tmp/wml.1183.tmp1.wml line \& 5| 10, near ""attrs:<: print attrs:0; print "\e" \& 6| (Missing operator before \e?) \& 7| syntax error at /tmp/wml.1183.tmp1.wml line 10, near ""attrs:<: print \& 8| attrs:0; print "\e" \& 9| Execution of /tmp/wml.1151.tmp1.wml aborted due to compilation errors. \& 10| \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- \& 11| ** WML:Break: Error in Pass 3 (rc=74). .Ve .PP Huh, looks like something went wrong. Output after pass 2 is .PP .Vb 1 \& 1| <: print "attrs:<: print attrs:0; :>"; :> .Ve .PP And because ePerl commands cannot be nested, an error is reported (if you do not understand why we have this text after pass 2, reread previous section). .PP This example is simplistic, and a workaround is trivial (use \&\f(CW\*(C`\*(C'\fR instead), but there are many cases where these problems are much more difficult to track. For instance if you nest macros defined in \s-1WML\s0 modules, you do not know whether they use ePerl code or not. .SS "First try to solve this problem" .IX Subsection "First try to solve this problem" One problem is that ePerl commands cannot be nested, according to its documentation. So our first try is to count nested levels and print ePerl delimiters when in outer mode only. .PP Input: .PP .Vb 10 \& 1| \e \& 2| \& 3| \& 4| 1 />> \& 5| <: %body :> \& 6| \& 7| 1 />> \& 8| %body \& 9| \& 10| \& 11| \e \& 12| \e \& 13| $a += 1; %body\e \& 14| \e \& 15| \& 16| <:= $a :> .Ve .PP Output: .PP .Vb 2 \& 1| \& 2| 3 .Ve .PP Another example (lines 1\-11 are left unchanged) .PP Input: .PP .Vb 6 \& 12| \& 13| \& 14| $string = q|%body|; $string =~ s|%0||g; print $string; \& 15| \& 16| \e \& 17| Hello this is a test .Ve .PP Output: .PP .Vb 1 \& 1| Hllo this is a tst .Ve .PP With previous definitions, here is what happens when nesting \&\f(CW\*(C`\*(C'\fR tags: .PP Input: .PP .Vb 3 \& 17| \e \& 18| Hello this is a test\e \& 19| .Ve .PP Output: .PP .Vb 10 \& 1| ePerl:Error: Perl parsing error (interpreter rc=255) \& 2| \& 3| \-\-\-\- Contents of STDERR channel: \-\-\-\-\-\-\-\-\- \& 4| Bareword found where operator expected at /tmp/wml.1198.tmp1.wml \& 5| line 10, near "q|$string = q|Hello" \& 6| syntax error at /tmp/wml.1198.tmp1.wml line 10, near "q|$string = \& 7| q|Hello this "syntax error at /tmp/wml.1198.tmp1.wml line 10, near ";|" \& 8| Execution of /tmp/wml.1198.tmp1.wml aborted due to compilation errors. \& 9| \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- \& 10| ** WML:Break: Error in Pass 3 (rc=74). .Ve .PP To understand why this error is reported, we run only the first two passes to see which input is sent to ePerl: .PP .Vb 3 \& prompt$ wml \-q \-p12 qaz.wml \& <: $string = q|$string = q|Hello this is a test|; $string =~ s|e||g; \& print $string;|; $string =~ s|s||g; print $string; :> .Ve .PP As expected ePerl delimiters are only put around the whole sentence, and are not nested. But we can see this is not sufficient, because the \&\f(CW%body\fR directive was replaced by ePerl code, and not a string. .PP In one word, there will be trouble whenever special sequences (\f(CW\*(C`%\*(C'\fR, \f(CW%body\fR, \f(CW%attributes\fR, ...) appear within ePerl delimiters, because you can not ensure that replacement text does not contain ePerl commands too. .SS "Macros defined by the wml::std::tags module" .IX Subsection "Macros defined by the wml::std::tags module" The \fIwml::std::tags\fR(3) module provides a solution to deal with nested ePerl commands. Previous example may be written like this .PP Input: .PP .Vb 10 \& 1| #use wml::std::tags \& 2| \& 3| \& 4| \& 5| %body \& 6| %0 \& 7| $string =~ s|$letter||g; \& 8| \& 9| \& 10| \e \& 11| \e \& 12| Hello this is a test\e \& 13| .Ve .PP Output: .PP .Vb 4 \& ...61 empty lines... \& 62| Hllo thi i a tt \& 63| \& 64| .Ve .PP How this works is beyond the scope of this document, and we will focus on commands provided by the \fIwml::std::tags\fR module, and how to use them. In the list below, pseudo-perl commands show an equivalent form of these macros. .IP "" 2 .IX Item "" This macro expands to a Perl variable, which is different in all nested levels. .Sp .Vb 1 \& $perl_var .Ve .IP "string" 2 .IX Item "string" This complex tag prints its body. .Sp .Vb 1 \& print qq(string); .Ve .IP "" 2 .IX Item "" This simple tag prints its attributes. .Sp .Vb 1 \& print string; .Ve .IP "" 2 .IX Item "" Prints the \f(CW\*(C`\*(C'\fR variable .Sp .Vb 1 \& print $perl_var; .Ve .ie n .IP "value" 2 .el .IP "value" 2 .IX Item "value" Assign a Perl variable. If there is no attribute, value is assigned to \&\f(CW\*(C`\*(C'\fR. .Sp .Vb 1 \& $variable = qq(value); .Ve .ie n .IP "value" 2 .el .IP "value" 2 .IX Item "value" Assign a Perl variable. If there is no attribute, value is assigned to \&\f(CW\*(C`\*(C'\fR. .Sp .Vb 1 \& $variable = q(value); .Ve .SS "How to use these macros" .IX Subsection "How to use these macros" Now that we know our problem has a solution, you are certainly impatient to learn how to proceed. There are two golden rules: .IP "1." 2 Never write special sequences (\f(CW\*(C`%\*(C'\fR, \f(CW%body\fR, \&\f(CW%attributes\fR, ...) inside a Perl statement. .IP "2." 2 Never use the Perl \f(CW\*(C`print\*(C'\fR statement, nor its derivatives. .PP First rule tells to replace .PP .Vb 2 \& $var1 = qq|%body|; \& $var2 = q|%body|; .Ve .PP by .PP .Vb 2 \& %body \& %body .Ve .PP and second rule .PP .Vb 2 \& print $string; \& print "\e"$alt\e""; .Ve .PP by .PP .Vb 2 \& \& $alt .Ve .SS "Examples" .IX Subsection "Examples" Example 1: simplified version of \f(CW\*(C`wml::des::lowsrc\*(C'\fR .PP Non-nestable version: .PP .Vb 11 \& \& <: \& { \& my $src = \*(Aq%0\*(Aq; \& my $lowsrc = $src; \& $lowsrc =~ s|\e.([^.]+)$|.lowsrc.$1|; \& system("convert \-monochrome $src $lowsrc"); \& print "lowsrc=\e"$lowsrc\e""; \& } \& :> \& .Ve .PP Nestable version: .PP .Vb 12 \& \& \& { \& my $src; \& %0 \& my $lowsrc = $src; \& $lowsrc =~ s|\e.([^.]+)$|.lowsrc.$1|; \& system("convert \-monochrome $src $lowsrc"); \& lowsrc="$lowsrc" \& } \& \& .Ve .PP The first change (assignment to \f(CW$src\fR) allows attribute to be an ePerl command, and second change (print result) allows this macro to appear inside ePerl commands. As you see, this is fairly straightforward, and you may look how \s-1WML\s0 modules are written. .PP In all previous examples and definitions, output was printed to standard output. But sometimes it is printed to filehandles. Here is how to proceed, with an example taken from \f(CW\*(C`wml::fmt::xtable\*(C'\fR. .PP Non-nestable version: .PP .Vb 10 \& \& <: \& { \& my $options = qq|%attributes|; \& my $tmpfile = "/wml.table.$$.tmp"; \& local (*FP); \& open(FP, ">$tmpfile"); \& print FP "<" . "wwwtable $options>\en"; \& print FP <<\*(Aq_\|_XTABLE_\|_EOT\*(Aq \& %body \& _\|_XTABLE_\|_EOT \& ; \& print FP "<" . "/wwwtable>\en"; \& close(FP); \& open(FP, "$WML_LOC_LIBDIR/exec/freetable \-w $tmpfile|"); \& local ($/) = undef; \& print ; \& close(FP); \& unlink("$tmpfile"); \& } \& :> \& .Ve .PP Nestable version: .PP .Vb 10 \& \& \& \& \& { \& my $tmpfile = "/wml.table.$$.tmp"; \& my $options; \& %attributes; \& 1 />> \& local *FH_XTABLE; \& open(FH_XTABLE, ">$tmpfile"); \& \& \& \& %body \& \& \& \& # we cut here to change filehandle \& \& 1 />> \& print FH_XTABLE ; \& close(FH_XTABLE); \& open(FH_XTABLE_IN, \& "/exec/freetable \-w $tmpfile |"); \& local ($/) = undef; \& # The asterisk below prevents expansion during pass 2 and is \& # removed after this pass. \& = <*FH_XTABLE_IN>; \& close(FH_XTABLE_IN); \& \& unlink("$tmpfile"); \& \& } \& \& \& .Ve .PP Filehandles are defined via attributes to the \f(CW\*(C`perl\*(C'\fR tag. All subsequent calls to \f(CW\*(C`\*(C'\fR are then printed to this filehandle. .SH "AUTHOR" .IX Header "AUTHOR" .Vb 2 \& Denis Barbier \& barbier@engelschall.com .Ve