MP4H - Un macro processeur pour HTML

Introduction

Le programme mp4h est un macro-processeur spécialement conçu pour traiter les documents HTML. Il possède des capacités puissantes de programmation, avec une syntaxe familière aux auteurs de documents HTML.

Ce programme est dérivé de Meta-HTML, écrit par Brian J. Fox, Il en reprend une partie de l'interface utilisateur, mais pas le code source. En effet, Meta-HTML était utilisé à l'intérieur du programme WML (Website Meta Language) écrit par Ralf S. Engelschall et que je maintiens depuis janvier 1999. Pour des raisons de licences, il était difficile de faire des modifications dans Meta-HTML, et j'ai décidé d'écrire mon propre macro-processeur.

Plutôt que de tout réécrire depuis le début, j'ai utilisé en grande partie le code source de GNU m4, écrit par René Seindal, en raison de ses nombreux avantages : ce logiciel est stable, robuste et très bien documenté. Cette version de mp4h s'appuye sur la version 1.4n de GNU m4, qui est une version de développement.

Le programme mp4h a été conçu dans le seul but de pouvoir définir ses propres balises dans un document HTML, et n'est en aucune façon un éditeur HTML. Ce n'est donc pas la peine de demander d'ajouter des fonctionnalités pour modifier la mise en page du document final, il y a peu de chances que je le fasse. Pour avoir un résultat correct, il suffit d'utiliser un post-processeur comme tidy.

Options de la ligne de commande

Les arguments optionnels sont placés entre crochets. Tous les synonymes d'une option ont la même syntaxe, donc lorsqu'une option longue accepte un argument, l'option courte doit l'accepter aussi.

La syntaxe générale d'appel de mp4h est

mp4h [options] [fichier [fichier] ...]
Les options sont décrites ci-dessous. Si aucun fichier n'est spécifié sur la ligne de commande, ou s'il a pour nom -, alors les caractères sont lus sur l'entrée standard.

Mode opératoire

--help Affiche un message d'aide et quitte le programme
--version Affiche le numéro de version de mp4h et quitte le programme
-E --fatal-warnings Arrête l'exécution du programme dès qu'un message d'avertissement est affiché
-Q --quiet --silent Supprime certains messages d'avertissement

Pré-processeur

-I --include=REPERTOIRE Ajoute ce répertoire à la liste des répertoires où les fichiers à inclure sont cherchés
-D --define=NOM[=VALEUR] Affecte une valeur à la variable de nom NOM
-U --undefine=COMMANDE Supprime la primitive de nom COMMANDE

Limites des ressources

-H --hashsize=PREMIER Utilise le nombre premier PREMIER pour remplir la table de hachage (défaut: 509)
-L -nesting-limit=NOMBRE Fixe la limite arbitraire du nombre d'appels récursifs (défaut: 250)

Déboguage

-d --debug=FLAGS Détermine quelles sont les informations affichées (voir ci-dessous)
-t --trace=NOM La commande NOM sera tracée dès qu'elle est définie
-l --arglength=NOMBRE Limite le nombre de caractères affichés dans les messages de déboguage
-o --error-output=FICHIER Redirige dans un fichier les messages de déboguage

Les drapeaux pour l'option -d sont les suivantes:

t trace tous les appels de macros, pas seulement ceux déclarés avec debugging-on
a affiche les arguments
e affiche le texte de remplacement
c affiche la commande avant la collecte des arguments, après et après l'appel de la fonction
x ajoute un identifiant à la sortie, ce qui est utile en cas d'appel récursif
f affiche le nom du fichier d'entrée
l affiche le numéro de la ligne dans le fichier d'entrée
p affiche le résultat de la recherche de fichiers dans l'arborescence
i affiche les changements de fichiers d'entrée
V raccourci pour tous ces drapeaux

Description

Le programme mp4h est un macro-processeur, ce qui signifie que les mots-clés définis sont remplacés par un autre texte. Ce chapitre est un exposé des primitives définies par mp4h. Comme mp4h a été spécialement conçu pour les documents HTML, la syntaxe est très proche de celle du HTML, avec des balises et des attributs. La seule notion inconnue en HTML est la possibilité de commenter une partie du texte jusqu'à la fin de la ligne. Les commentaires commencent par trois point-virgules, par exemple:

;;;  Ceci est un commentaire

Fonctions pour les définitions

La définition de nouvelles commandes est la tâche la plus courante réalisée par mp4h. Comme en HTML, le nom des commandes peut être écrit indifféremment en monuscules ou majuscules. Dans cette documentation, seules les minuscules seront utilisées. Les commandes (aussi appelées balises), sont de deux types : simples ou complexes. Une balise simple a la forme suivante :

<nom [attributs]>
tandis qu'une balise complexe est de la forme :
<nom [attributs]>
texte
</nom>

  Dans les descriptions de commandes, une barre oblique / indique que la balise est complexe, et la lettre V que les attributs de la balise sont lus sans être développés (voir le chapitre sur le mécanisme de développement des macros pour plus de détails).

 

/   define-tag
nom
[attributes=verbatim]
[endtag=required]
[whitespace=delete]

Cette fonction permet de définir ses propres commandes. Le premier argument est le nom de la nouvelle commande. Le texte de remplacement est le corps de la fonction.

Code source
<define-tag foo>bar</define-tag>
<foo>
Résultat
bar
Même si en HTML les espaces ont en général peu d'importance, il est utile de comprendre certains points. En particulier,
<define-tag foo>bar</define-tag>
et
<define-tag foo>
bar
</define-tag>
ne sont pas équivalents, la deuxième forme comprenant deux sauts de lignes non présents dans la première.

 

/   provide-tag
nom
[attributes=verbatim]
[endtag=required]
[whitespace=delete]

Cette commande est identique à la précédente, sauf qu'aucune opération n'est effectuée si la commande est déjà définie.

 

    let
nouveau ancien

Copie une fonction dans une autre. Cette fonction permet notamment de sauvegarder une commande avant de la redéfinir.

Code source
<define-tag foo>un</define-tag>
<let bar foo>
<define-tag foo>deux</define-tag>
<foo><bar>
Résultat
deuxun

 

    undef
nom

Supprime la définition d'une commande.

Code source
<define-tag foo>un</define-tag>
<undef foo>
<foo>
Résultat
<foo>

 

/   set-hook
nom
[position=[before|after]
[action=insert|append|replace]

Ajoute du texte à une commande déjà définie. Cela permet de modifier des commandes existantes, sans avoir à se préoccuper si cette commande est complexe ou non.

Code source
<let foo add>
<set-hook foo position=before>
Avant</set-hook>
<set-hook foo position=after>
Après</set-hook>
<foo 1 2 3 4>
Résultat
Avant10
Après

 

    get-hook
nom
[position=[before|after]

Affiche le texte ajouté à cette commande avec set-hook.

Code source
Texte inséré avec position=before:<get-hook foo position=before>!
Texte inséré avec position=after:<get-hook foo position=after>!
Résultat
Texte inséré avec position=before:
Avant!
Texte inséré avec position=after:
Après!

Variables

Les variables peuvent être vues comme un cas particulier de fonctions, qui ne possèdent pas d'arguments. En fait, leur usage est différent, notamment grâce à la notion de tableaux. En effet, une variable peut être considérée comme un tableau de caractères séparés par des sauts de lignes. Comme nous le verrons ci-dessous, cette notion permet des manipulations qui sont difficilement accessibles avec des fonctions.

 

    set-var
nom[=valeur]
[nom[=valeur]] ...

Cette commande permet de définir des variables.

 

  V set-var-verbatim
nom[=valeur]
[nom[=valeur]] ...

Comme la commande précédente, mais les arguments sont lus tels quels, sans développement.

 

    get-var
nom
[nom] ...

Affiche le contenu de la variable. Lorsqu'une valeur numérique est indiquée entre crochets, elle représente un indice (par convention, la première valeur d'un tableau correspond à un indice 0).

Code source
<set-var version="0.10.1">
Ceci est la version <get-var version>
Résultat
Ceci est la version 0.10.1
Code source
<set-var foo="0
1
2
3">
<get-var foo[2] foo[0] foo>
Résultat
200
1
2
3

 

  V get-var-once
nom
[nom] ...

Comme la commande précédente, mais les arguments ne sont pas développés.

Code source
<define-tag foo>0.10.1</define-tag>
<set-var version="<foo>">;;;
Ici version <get-var version>
<set-var-verbatim version="<foo>">;;;
Ici version <get-var version>
<set-var-verbatim version="<foo>">;;;
Ici version <get-var-once version>
Résultat
Ici version 0.10.1
Ici version 0.10.1
Ici version <foo>

 

    preserve
nom

Une pile est utilisée pour sauvegarder le contenu de variables. Cette commande permet de placer le contenu de la variable dont le nom est passé en argument dans le sommet de la pile. Après appel de cette commande, la variable est réinitialisée, c'est-à-dire que son contenu est vide.

 

    restore
nom

Cette commande effectue l'opération inverse de la commande précédente ; la variable dont le nom est passé en argument prend la valeur qui se trouve au sommet de la pile, et cette valeur est supprimée de la pile.

 

    unset-var
nom
[nom] ...

Déclare la variable passée en argument comme n'étant pas définie.

 

    var-exists
nom

Retourne true si la variable existe, et rien sinon.

 

    increment
nom
[by=valeur]

Incrémente le contenu de la variable passée en argument. L'incrément par défaut est 1.

Code source
<set-var i=10>
<get-var i>
<increment i><get-var i>
<increment i by="-3"><get-var i>
Résultat
10
11
8

 

    decrement
nom
[by=valeur]

Decrémente le contenu de la variable passée en argument, par défaut de 1.

Code source
<set-var i=10>
<get-var i>
<decrement i><get-var i>
<decrement i by="3"><get-var i>
Résultat
10
9
6

 

    copy-var
src
dest

Copie le contenu d'une variable dans une autre.

Code source
<set-var i=10>
<copy-var i j>
<get-var j>
Résultat
10

 

    defvar
nom
valeur

Si la variable n'est pas définie ou est définie comme une chaîne de caractères vide, affecte la valeur passée en argument. Dans le cas contraire, aucune opération n'est effectuée.

Code source
<unset-var title>
<defvar title "Titre"><get-var title>
<defvar title "Nouveau titre"><get-var title>
Résultat
Titre
Titre

 

    symbol-info
nom

Affiche des informations sur le symbole passé en argument. S'il s'agit d'une variable, le mot STRING est affiché, ainsi que le nombre de lignes de cette variable. S'il s'agit d'une commande ou primitive, un message indique de quel type de symbole il s'agit : PRIM COMPLEX, PRIM TAG, USER COMPLEX et USER TAG

Code source
<set-var x="0\n1\n2\n3\n4">
<define-tag foo>bar</define-tag>
<define-tag bar endtag=required>quux</define-tag>
<symbol-info x>
<symbol-info symbol-info>
<symbol-info define-tag>
<symbol-info foo>
<symbol-info bar>
Résultat
STRING
5
PRIM TAG
PRIM COMPLEX
USER TAG
USER COMPLEX

Manipulation de chaînes de caractères

 

    string-length
chaîne

Retourne le nombre de caractères présents dans la chaîne. Les sauts de ligne comptent pour un caractère.

Code source
<set-var foo="0
1
2
3">;;;
<string-length <get-var foo>>
<set-var foo="0 1 2 3">;;;
<set-var l=<string-length <get-var foo>>>;;;
<get-var l>
Résultat
7
7

 

    downcase
chaîne

Convertit les caractères en minuscules.

Code source
<downcase "Qu'en est-il des caractères accentués ?">
Résultat
qu'en est-il des caractères accentués ?

 

    upcase
chaîne

Convertit les caractères en majuscules.

Code source
<upcase "Quid des caractères accentués ?">
Résultat
QUID DES CARACTÈRES ACCENTUÉS ?

 

    capitalize
chaîne

Convertit une phrase en titre anglo-saxon, avec une capitale en début de chaque mot.

Code source
<capitalize "Cet éléphant est-il fou ?">
Résultat
Cet Éléphant Est-il Fou ?

 

    substring
chaîne
[début [fin]]

Extrait une partie de la chaîne de caractères. Le premier argument est la chaîne à traiter, les deuxième et troisième sont les indices respectivement du début et de la fin. Par convention, le premier caractère est représenté par l'indice 0.

Code source
<set-var foo="abcdefghijk">
<substring <get-var foo> 4>
<substring <get-var foo> 4 6>
Résultat
efghijk
ef

 

    subst-in-string
chaîne
regexp
[remplacement]
[singleline=true]

Effectue une substitution dans la chaîne de caractères.

Code source
<set-var foo="abcdefghijk">
<subst-in-string <get-var foo> "[c-e]">
<subst-in-string <get-var foo> "([c-e])" "\\1 ">
Résultat
abfghijk
abc d e fghijk
Code source
<set-var foo="abcdefghijk\nabcdefghijk\nabcdefghijk">
<subst-in-string <get-var foo> ".$" "">
<subst-in-string <get-var foo> ".$" "" singleline=true>
Résultat
abcdefghij
abcdefghij
abcdefghij
abcdefghijk
abcdefghijk
abcdefghij

 

    subst-in-var
nom
regexp
[remplacement]
[singleline=true]

Effectue une substitution dans une variable.

 

    string-eq
chaîne1
chaîne2
[caseless=true]

Si les arguments 1 et 2 sont identiques, retourne true, sinon aucune valeur n'est affichée.

Code source
1:<string-eq "aAbBcC" "aabbcc">
2:<string-eq "aAbBcC" "aAbBcC">
Résultat
1:
2:true
Code source
1:<string-eq "aAbBcC" "aabbcc" caseless=true>
2:<string-eq "aAbBcC" "aAbBcC" caseless=true>
Résultat
1:true
2:true

 

    string-neq
chaîne1
chaîne2
[caseless=true]

Si les arguments 1 et 2 sont identiques, aucune valeur n'est affichée, sinon retourne true.

Code source
1:<string-neq "aAbBcC" "aabbcc">
2:<string-neq "aAbBcC" "aAbBcC">
Résultat
1:true
2:
Code source
1:<string-neq "aAbBcC" "aabbcc" caseless=true>
2:<string-neq "aAbBcC" "aAbBcC" caseless=true>
Résultat
1:
2:

 

    string-compare
chaîne1
chaîne2
[caseless=true]

Compare deux chaînes de caractères et retourne une des valeurs less, greater ou equal suivant que le premier argument est inférieur, supérieur, ou égal au second.

Code source
1:<string-compare "aAbBcC" "aabbcc">
2:<string-compare "aAbBcC" "aAbBcC">
Résultat
1:less
2:equal
Code source
1:<string-compare "aAbBcC" "aabbcc" caseless=true>
Résultat
1:equal

 

    match
chaîne
regexp
[caseless=true]
[action=report|extract|delete|startpos|endpos|length]
Code source
1:<match "abcdefghijk" "[c-e]+">
2:<match "abcdefghijk" "[c-e]+" action=extract>
3:<match "abcdefghijk" "[c-e]+" action=delete>
4:<match "abcdefghijk" "[c-e]+" action=startpos>
5:<match "abcdefghijk" "[c-e]+" action=endpos>
6:<match "abcdefghijk" "[c-e]+" action=length>
Résultat
1:true
2:cde
3:abfghijk
4:2
5:5
6:3

 

    char-offsets
chaîne
caractère
[caseless=true]

Affiche un tableau contenant les indices où le caractère passé en second argument apparait dans la chaîne de caractères passée en premier argument.

Code source
1:<char-offsets "abcdAbCdaBcD" a>
2:<char-offsets "abcdAbCdaBcD" a caseless=true>
Résultat
1:0
8
2:0
4
8

 

    set-regexp-syntax
[type=basic|extended]

Cette commande permet de contrôler quel type d'expression régulière est utilisé dans les différentes commandes ci-dessus. Actuellement, deux types sont définis: basic et extended. Le premier correspond aux expressions régulières de la norme POSIX, le second aux expressions régulières étendues. Par défaut, les expressions régulières sont de type extended.

Code source
<set-var foo="abcdefghijk">
<set-regexp-syntax type=basic>
<subst-in-string <get-var foo> "([c-e]+)" ":\\1:">
<subst-in-string <get-var foo> "\\([c-e]\\{1,\\}\\)" ":\\1:">
<set-regexp-syntax type=extended>
<subst-in-string <get-var foo> "([c-e]+)" ":\\1:">
<subst-in-string <get-var foo> "\\([c-e]\\{1,\\}\\)" ":\\1:">
Résultat
abcdefghijk
ab:cde:fghijk

ab:cde:fghijk
abcdefghijk

 

    get-regexp-syntax

Affiche quel est le type d'expressions régulières actuellement utilisé.

Code source
<get-regexp-syntax>
Résultat
extended

Tableaux

Le programme mp4h permet de faire simplement des manipulations sur les tableaux. Le texte contenu à l'intérieur d'une variable peut être vu indifféremment comme une seule chaîne de caractères, ou comme un tableau de lignes séparées par des sauts de ligne. Ainsi, après

<set-var chiffres="0
1
2
3">
nous pouvons afficher soit le contenu de cette variable, soit une des lignes.
Code source
<get-var chiffres>
<get-var chiffres[2]>
Résultat
0
1
2
3
2

 

    array-size
nom

Affiche la taille du tableau dont le nom est passé en argument. Cette taille est le nombre de lignes présentes dans la variable.

Code source
<array-size chiffres>
Résultat
4

 

    array-push
nom
valeur

Ajoute une ou plusieurs lignes à la fin du tableau.

Code source
<array-push chiffres "10\n11\n12">
<get-var chiffres>
Résultat
0
1
2
3
10
11
12

 

    array-pop
nom

Enlève la dernière ligne du tableau et l'affiche.

 

    array-add-unique
nom
valeur
[caseless=true]

Ajoute une valeur à la fin du tableau si elle n'est pas déjà présente dans ce tableau.

Code source
<array-add-unique chiffres 2>
<get-var chiffres>
Résultat
0
1
2
3
10
11
12

 

    array-concat
nom1
[nom2] ...

Concatène le contenu des tableaux dans la variable dont le nom est passé en premier.

Code source
<set-var foo="foo">
<set-var bar="bar">
<array-concat foo bar><get-var foo>
Résultat
foo
bar

 

    array-member
nom
valeur
[caseless=true]

Si la valeur est contenue dans le tableau, retourne l'indice de cette valeur, sinon retourne -1.

Code source
<array-member chiffres 11>
Résultat
5

 

    array-shift
nom
offset
[start=début]

Cette commande permet de décaler les éléments du tableau. Le premier argument est la valeur du décalage, et le second argument le nom de la variable à manipuler. Si le décalage est négatif, les indices qui tombent en dessous de 0 sont éliminés, et s'il est positif, les premières valeurs sont remplies par des lignes vides.

Code source
<array-shift chiffres 2>
Maintenant: <get-var chiffres>
<array-shift chiffres -4>
Et là: <get-var chiffres>
Résultat
Maintenant: 

0
1
2
3
10
11
12

Et là: 2
3
10
11
12

 

    sort
nom
[caseless=true]
[numeric=true]
[sortorder=reverse]

Trie les lignes d'un tableau par ordre alphabétique.

Code source
<sort chiffres><get-var chiffres>
Résultat
12
2
3

Opérateurs arithmétiques

Les opérateurs suivants permettent de manipuler des nombres. Lorsque tous les nombres sont des entiers, le résultat est un entier. Dans le cas contraire, le résultat est un réel.

 

    add
nombre1
nombre2
[nombre3] ...

 

    substract
nombre1
nombre2
[nombre3] ...

 

    multiply
nombre1
nombre2
[nombre3] ...

 

    divide
nombre1
nombre2
[nombre3] ...

 

    min
nombre1
nombre2
[nombre3] ...

 

    max
nombre1
nombre2
[nombre3] ...
Code source
<add 1 2 3 4 5 6>
<add 1 2 3 4 5 6.>
Résultat
21
21.000000
Code source
<define-tag factorielle whitespace=delete>
<ifeq %0 1 1 <multiply %0 "<factorielle <substract %0 1>>">>
</define-tag>
<factorielle 6>
Résultat
720

 

    modulo
nombre1
nombre2

Contrairement aux fonctions ci-dessus, la fonction modulo ne peut prendre que deux arguments, qui sont obligatoirement des entiers.

Code source
<modulo 345 7>
Résultat
2

Les fonctions suivantes permettent de comparer des nombres, et retournent true lorsque la comparaison est vraie et rien sinon. Si un des arguments n'est pas un nombre, la comparaison est fausse.

 

    gt
nombre1
nombre2

Retourne true si le premier argument est strictement supérieur au second.

 

    lt
nombre1
nombre2

Retourne true si le premier argument est strictement inférieur au second.

 

    eq
nombre1
nombre2

Retourne true si les deux arguments sont égaux.

 

    neq
nombre1
nombre2

Retourne true si les deux arguments sont différents.

Opérateurs relationnels

 

    not
chaîne

Retourne true si la chaîne de caractères est vide, et rien sinon.

 

    and
chaîne
[chaîne] ...

Retourne le dernier argument si tous les arguments sont non vides, sinon ne retourne rien.

 

    or
chaîne
[chaîne] ...

Affiche le premier argument non nul.

Fonctions de contrôle

 

  V group
commande
[commande] ...
[separator=chaîne]

Cette fonction a pour objectif de grouper une succession de commandes en une seule. Des applications seront vues dans les exemples des commandes suivantes.

Une autre utilisation de cette commande, moins intuitive mais tout aussi importante, est de pouvoir mettre des sauts de ligne même si l'attribut whitespace=delete a été spécifié.

Code source
<define-tag texte1>
Ce texte est sur
3 lignes sans
whitespace=delete
</define-tag>
<define-tag texte2 whitespace=delete>
Ce texte est sur
3 lignes avec
whitespace=delete
</define-tag>
<define-tag texte3 whitespace=delete>
<group "Ce texte est sur
3 lignes avec
whitespace=delete">
</define-tag>
<texte1>
<texte2>
<texte3>
Résultat
Ce texte est sur
3 lignes sans
whitespace=delete

Ce texte est sur3 lignes avecwhitespace=delete
Ce texte est sur
3 lignes avec
whitespace=delete

Notez dans l'exemple 2 que les sauts de ligne ont été supprimés, ce qui a pour conséquence de coller des lettres qui ne doivent pas l'être.

 

  V noexpand
commande
[commande] ...

Cette fonction affiche ses arguments sans les développer. Ils ne seront ensuite plus jamais développés, à moins d'utiliser la commande expand.

 

    expand
commande
[commande] ...

Annule l'effet de la balise noexpand.

Code source
<subst-in-string "=LT=define-tag foo>bar</define-tag>" "=LT=" "<">
<foo>
<set-var a=<subst-in-string "=LT=define-tag foo>quux</define-tag>" "=LT=" "<noexpand "<">">>
<foo>
<get-var a>
<expand <get-var a>>
<foo>
Résultat
bar

bar
<define-tag foo>quux</define-tag>

quux

 

  V if
chaîne
then-clause
[else-clause]

Si la chaîne de caractères passée en premier argument n'est pas vide, le deuxième argument est exécuté, sinon c'est le troisième.

Code source
<define-tag test whitespace=delete>
<if %0 "oui" "non">
</define-tag>
<test "chaîne">
<test "">
Résultat
oui
non

 

  V ifeq
chaîne1
chaîne2
then-clause
[else-clause]

Si les deux chaînes de caractères passées en premier et deuxième arguments sont identiques, le troisième argument est exécuté, sinon c'est le quatrième.

 

  V ifneq
chaîne1
chaîne2
then-clause
[else-clause]

Si les deux chaînes de caractères passées en premier et deuxième arguments ne sont pas identiques, le troisième argument est exécuté, sinon c'est le quatrième.

 

/   when
chaîne

Quand la chaîne de caractères passée en premier argument n'est pas vide, le corps de cette fonction est exécuté.

 

/ V while
condition

Tant que la condition passée en premier argument est vérifiée, le corps de cette fonction est exécuté.

Code source
<set-var i=10>
<while <gt <get-var i> 0>>;;;
  <get-var i> <decrement i>;;;
</while>
Résultat
10 9 8 7 6 5 4 3 2 1 

 

/   foreach
variable
array
[start=début]
[end=fin]
[step=pas]

Cette commande est similaire à la commande foreach de Perl ; une variable prend successivement toutes les valeurs stockées dans un tableau, et le corps de la fonction est exécuté pour chacune de ces valeurs. Le premier argument est le nom de la variable qui va prendre les différentes valeurs, et le deuxième argument est le nom de la variable qui contient le tableau de valeurs.

Code source
<set-var x="1\n2\n3\n4\n5\n6">
<foreach i x><get-var i> </foreach>
Résultat
1 2 3 4 5 6 

 

  V var-case
var1=valeur1 action1
[var2=valeur2 action2 ...

Cette commande permet de faire plusieurs tests en une seule instruction.

Code source
<set-var i=0>
<define-tag test>
<var-case x=1 <group <increment i> x<get-var i>> x=2 <group <decrement i> x<get-var i>> y=1 <group <increment i> y<get-var i>> y=2 <group <decrement i> y<get-var i>>>
</define-tag>
<set-var x=1 y=2><test>
<set-var x=0 y=2><test>
Résultat
x1y0


y-1

 

    break

Cette commande fait sortir de la boucle while la plus interne.

Code source
<set-var i=10>
<while <gt <get-var i> 0>>;;;
  <get-var i> <decrement i>;;;
  <ifeq <get-var i> 5 <break>>;;;
</while>
Résultat
10 9 8 7 6 

 

    return
[up=numéro]
chaîne

Cette commande fait sortir de la commande la plus interne. Éventuellement, un message est affiché dans le texte. Mais comme cette commande modifie le processus normal de lecture, son emploi peut engendrer des erreurs très difficiles à détecter.

 

    warning

Affiche le message donné en arguments sur l'erreur standard.

 

    exit
[message=chaîne]
[status=rc]

Interrompt immédiatement le programme.

 

    at-end-of-file

Cette commande est spéciale, puisqu'elle stocke ses arguments temporairement. Ils seront traités lorsque le fichier d'entrée est entièrement lu.

Fonctions sur les fichiers

 

    directory-contents
répertoire
[matching=regexp]

Le nom d'un répertoire est passé en argument. Cette commande affiche la liste des noms de fichiers présents dans ce répertoire, séparés par des sauts de ligne.

Code source
<directory-contents . matching=".*\\.mp4h$">
Résultat
mp4h.mp4h

 

    file-exists
fichier

Affiche true si le fichier dont le nom est passé en argument est trouvé, et rien sinon.

 

    get-file-properties
fichier

Affiche un tableau d'informations sur le fichier dont le nom est donné en argument. Les informations sont dans l'ordre : la taille du fichier, son type, la date du dernier changement de l'inode (en secondes depuis le 1er janvier 1970), la date de la dernière modification du fichier, la date du dernier accès au fichier, le nom du propriétaire du fichier, et le nom du groupe.

Code source
<get-file-properties <__file__>>
Résultat
51289
FILE
954610129
954610129
954610158
barbier
users

 

    include
fichier
[alt=action]
[verbatim=true]

Lit les données contenues dans le fichier passé en argument.

 

/   comment

Le corps de cette balise est supprimé.

 

    set-eol-comment
[chaîne]

Permet de modifier les caractères servant de commentaires.

Aide au déboguage

Lorsque les constructions deviennent complexes, il peut être ardu de comprendre ce qui cloche dans ce qu'on a écrit. Les fonctions ci-dessous permettent d'afficher certaines informations. Le déboguage reste cependant délicat, et est une partie de mp4h à améliorer.

 

    function-def
nom

Affiche le texte de remplacement de la fonction. Par exemple, la commande qui affiche les exemples à été définie ainsi:

Code source
<function-def example>
Résultat
<set-var-verbatim verb-body=%ubody>
<subst-in-var verb-body "&" "&amp;">
<subst-in-var verb-body "<" "&lt;">
<subst-in-var verb-body ">" "&gt;">
<set-var body=%body>
<subst-in-var body "&" "&amp;">
<subst-in-var body "<" "&lt;">
<subst-in-var body ">" "&gt;">
<subst-in-var body "<three-colon>\n[ \t]*" "" singleline=true>
<subst-in-var body "<three-colon>[^;][^\n]*\n[ \t]*" "" singleline=true>
<subst-in-var body "<three-colon>$" "">
<subst-in-var body "^\n+" "" singleline=true>
<table border=2 cellpadding=0 cellspacing=0 width="80%" summary="">
    <tr><th bgcolor="#ccccff"><lang:example-source></th></tr>
    <tr><td bgcolor="#ccff99" width="80%"><dnl>
      <pre><get-var-once verb-body></pre><dnl>
    </td></tr>
    <tr><th bgcolor="#ccccff"><lang:example-output></th></tr>
    <tr><td bgcolor="#ff99cc" width="80%"><dnl>
      <pre><get-var-once body></pre><dnl>
    </td></tr>
</table>

 

    debugmode
chaîne

Cette commande prend le même argument que l'option -d.

 

    debugfile
fichier

Indique dans quel fichier les instructions de déboguage doivent être écrites. Si le nom de fichier est vide, l'affichage de ces instructions se fait de nouveau dans la sortie erreur standard. Si le nom du fichier est -, l'affichage est supprimé.

Note: Il n'existe aucun moyen pour insérer ces instructions à l'intérieur du document post-traité.

 

    debugging-on
nom
[nom] ...

Les commandes dont les noms sont passés en argument sont «tracées», c'est-à-dire que les informations demandées au travers de l'option -d ou de la commande debugmode sont affichées lors de chaque appel de la fonction.

 

    debugging-off
nom
[nom] ...

Fonction inverse de la précédente.

Divers

 

    __file__
[fichier]

Sans argument, la fonction retourne le nom du fichier en train d'être traité. S'il y a un argument, il est le nom du fichier retourné par les prochains appels à cette fonxction.

 

    __line__
[numéro]

Sans argument, la fonction retourne le numéro de la ligne dans le fichier en train d'être traité. S'il y a un argument, il est le numéro de la ligne retourné par les prochains appels à cette fonxction.

Code source
Ceci est le fichier <__file__>, ligne <__line__>.
Résultat
Ceci est le fichier mp4h.mp4h, ligne 1682.

En fait, si vous regardez le fichier source, vous vous apercevrez que le numéro de la ligne n'est pas correct. En effet, les commandes sont lues avant d'être développées. Le numéro retourné est le numéro de la dernière ligne du bloc qui contient cette instrction.

 

    __version__

Affiche le numéro de version de mp4h.

 

    dnl

Le texte après cette balise est ignoré jusqu'au prochain saut de ligne. Cela permet de mettre un commentaire qui sera ignoré même si les caractères pour les commentaires ont été redéfinies avec la commande set-eol-comment.

Code source
<dnl>Ceci est un commentaire
foo
<dnl>Ceci est un commentaire
bar
Résultat
foo
bar

 

    date
[epoch]

Affiche l'heure locale correspondant au temps epoch donné en argument. Lorsqu'il n'y a pas d'argument, l'heure actuelle est affichée.

Code source
<date>
<set-var info=<get-file-properties <__file__>>>
<date <get-var info[2]>>
Résultat
Sat Apr  1 19:29:18 2000

Sat Apr  1 19:28:49 2000

 

    timer

Affiche le temps écoulé depuis le précédent appel à cette fonction. Le temps affiché est le nombre de cycles d'horloge, et est donc dépendant de la vitesse du processeur.

Code source
Depuis le début de la compilation de cette documentation par <mp4h>,
voici le nombre de cycles d'horloge écoulés:
<timer>
Résultat
Depuis le début de la compilation de cette documentation par <b>mp4h</b>,
voici le nombre de cycles d'horloge écoulés:
user 52
sys 3

 

    mp4h-l10n
nom=valeur

Redéfinit la localisation courante. Par défaut, le programme est lancé avec la localisation «C».

Code source
<mp4h-l10n LC_NUMERIC=fr_FR>
<add 1,2 3,4>
Résultat
4,600000

 

    mp4h-output-radix
number

Change le format d'affichage des nombres réels, en spécifiant le nombre de chiffres affichés après la virgule. Il y a par défaut six chiffres après la virgule.

Code source
<mp4h-l10n LC_NUMERIC=C>
<add 1.2 3.4>
<mp4h-output-radix 2>
<add 1.2 3.4>
Résultat
4.600000

4.60

Schéma d'expansion des macros

Cette partie décrit les mécanismes utilisés pour le remplacement des macros. Elle se veut aussi précise que possible, aussi n'hésitez pas à me signaler toute erreur ou omission.

Généralités

Commençons par donner des exemples de définition de balises:
Code source
<define-tag foo>
Ceci est un exemple de balise simple
</define-tag>
<define-tag bar endtag=required>
Ceci est un exemple de balise complexe
</define-tag>
<foo>
<bar>Corps de la fonction</bar>
Résultat
Ceci est un exemple de balise simple


Ceci est un exemple de balise complexe

Les commandes ainsi définies peuvent avoir des attributs, comme toute balise HTML. Pour que ces attributs se retrouvent dans le texte de remplacement, les conventions suivantes ont été adoptées:

Note: Le mécanisme de lecture des instructions est complètement différent dans Meta-HTML et dans mp4h. Avec Meta-HTML, il est parfois nécessaire d'avoir recours à d'autres constructions, comme %xbody et %qbody. Celles-ci n'ont pas lieu d'être avec mp4h, mais pour être au plus compatible avec Meta-HTML, mp4h les reconnait aussi et elles ont exactement le même rôle que %body. Une autre particularité non mentionnée ci-dessus, qui existe aussi pour des raisons de compatibilité avec Meta-HTML, est le fait que pour des balises simples, %body est un synonyme de %attributes. Ces fonctionnalités sont actuellement comprises dans mp4h, mais peuvent très bien être supprimées dans les versions futures.

Attributs

Les attributs sont séparés par un ou plusieurs espaces, tabulations ou sauts de lignes, et chaque attribut doit être une entité complète de mp4h. Par exemple, avec les définitionsa ci-dessus, <bar> ne peut pas être un attribut, parce que la commande <bar> est complexe. En revanche, il est possible d'écrire

<foo <foo>>
ou même
<foo <foo name=src url=ici>>
Dans les deux exemples ci-dessus, la commande foo a un seul attribut.

Dans certains cas, il est nécessaire de regrouper ensemble plusieurs chaînes de caractères pour en faire un seul attribut. Cela peut être fait soit avec des guillements anglo-saxons (double quotes), soit avec la commande group. Par exemple,

<foo "Ceci est le 1er attribut"
           <group et le second>>

Note: Contrairement au HTML, l'apostrophe ne peut pas servir à la place des guillements.

Si des guillements apparaissent dans un argument, ils doivent être précédés d'une barre oblique inverse (backslash) \ afin de ne pas être interprétés comme le caractère de fin de chaîne de caractères.

Code source
  <set-var texte="Texte avec un guillemet \" dedans">
  <get-var texte>
Résultat
  
  Texte avec un guillemet " dedans

Évaluation des commandes

Les commandes ont les caractéristiques suivantes:

Les caractères sont lus un par un, jusqu'à trouver un <. Le nom de la commande est ensuite lu. Les attributs sont lus, avec ou sans expansion, selon la caractéristique de la commande. Enfin, si la commande est complexe, le corps de la balise est lu. Le texte de remplacement est mis sur la pile, en remplaçaant les mots commençant par un %, comme %body, %attributes ainsi que %0, %1, etc.

Note: Par défaut, les attributs sont évalués avant la commande elle-même.

Considérons l'exemple suivant, qui permet de mettre du texte en police bâton:

<define-tag text-tt endtag=required whitespace=delete>
<tt>%body</tt>
</define-tag>

Cet exemple a un inconvénient, comme le montre l'exemple ci-dessous:

Code source
<text-tt>Ceci est un <text-tt>essai</text-tt></text-tt>
Résultat
<tt>Ceci est un <tt>essai</tt></tt>
Nous souhaiterions que les balises intérieures soient supprimées.

L'idée qui vient de suite est d'utiliser une variable pour savoir si on est déjà à l'intérieur d'une de ces balises:

<set-var _text:tt=0>
<define-tag text-tt endtag=required whitespace=delete>
<increment _text:tt>
<ifeq <get-var _text:tt> 1 "<tt>">
%body
<ifeq <get-var _text:tt> 1 "</tt>">
<decrement _text:tt>
</define-tag>
Code source
<text-tt>Ceci est un <text-tt>essai</text-tt></text-tt>
Résultat
<tt>Ceci est un essai</tt>

Mais si nous utilisons des balises simples, comme dans l'exemple ci-dessous, notre définition ne marche plus parce que les attributs sont développés avant la commande elle-même:

Code source
<define-tag opt><text-tt>%attributes</text-tt></define-tag>
<opt "Ceci est un <opt essai>">
Résultat
<tt>Ceci est un <tt>essai</tt></tt>

Pour remédier à ce problème, il convient donc d'empêcher que les attributs soient développés en premier. Il convient donc d'écrire:

Code source
<define-tag opt attributes=verbatim>;;;
<text-tt>%attributes</text-tt>;;;
</define-tag>
<opt "Ceci est un <opt essai>">
Résultat
<tt>Ceci est un essai</tt>

Auteur

Denis Barbier <barbier@imacs.polytechnique.fr>

Remerciement

Je tiens à remercier très sincèrement Brian J. Fox pour avoir écrit Meta-HTML et René Seindal pour son travail sur ce merveilleux macro-processeur qu'est GNU m4.