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.
- whitespace=delete
Certains espaces sont supprimés dans le texte de remplacement. Il
s'agit des espaces au début ou à la fin de la définition, et des
sauts de ligne qui ne sont pas compris à l'intérieur de commandes
délimitées par < et >.
- endtag=required
Définit une balise complexe
Code source |
<define-tag foo>bar</define-tag>
<foo>
|
Résultat |
bar
|
Code source |
<define-tag foo endtag=required>;;;
Le corps est : %body</define-tag>
<foo>Here it is</foo>
|
Résultat |
Le corps est : Here it is
|
- attributes=verbatim
Par défaut, les attributs sont évalués avant d'être insérés dans
le texte de remplacement. Lorsque cet attribut est utilisé, les
arguments sont insérés sans développement.
Code source |
<define-tag bar>quux</define-tag>
<define-tag foo attributes=verbatim endtag=required>
Corps: %Ubody
Attributs: %Uattributes
</define-tag>
<foo txt="<bar>">Et voila</foo>
|
Résultat |
Corps: Et voila
Attributs: txt="<bar>"
|
/ |
|
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.
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
|
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.
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
|
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>
|
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.
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.
Déclare la variable passée en argument comme n'étant pas
définie.
Retourne true si la variable existe, et rien sinon.
Incrémente le contenu de la variable passée en argument.
L'incrément par défaut est 1.
- by=valeur
Spécifie la valeur de l'incrément.
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
|
Decrémente le contenu de la variable passée en argument, par
défaut de 1.
- by=valeur
Spécifie la valeur qui est soustraite à la variable
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
|
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
|
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
|
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
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
|
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 ?
|
Convertit les caractères en majuscules.
Code source |
<upcase "Quid des caractères accentués ?">
|
Résultat |
QUID DES CARACTÈRES ACCENTUÉS ?
|
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 ?
|
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
|
- singleline=true
Lorsque cet attribut est utilisé, le symbole ^ représente
le début de la chaîne et le $ la fin de la chaîne. Sinon,
ils représentent respectivement un début de ligne et une fin de
ligne.
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
|
- caseless=true
Avec cet attribut, la comparaison est faite indépendamment de la
casse des caractères.
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:
|
- caseless=true
Avec cet attribut, la comparaison est faite indépendamment de la
casse des caractères.
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
|
- caseless=true
Avec cet attribut, la comparaison est faite indépendamment de la
casse des caractères.
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] |
|
- caseless=true
Avec cet attribut, la comparaison est faite indépendamment de la
casse des caractères.
- action=report
Affiche true si la chaîne de caractères contient
l'expression régulière, et rien sinon.
- action=extract
Affiche la partie de la chaîne de caractères qui correspond à
l'expression régulière.
- action=delete
Affiche la chaîne de caractères en supprimant la portion qui
correspond à l'expression régulière.
- action=startpos
Si la chaîne de caractères contient l'expression régulière,
affiche l'indice du premier caractère de la chaîne qui correspond à
l'expression régulière, sinon affiche -1.
- action=endpos
Si la chaîne de caractères contient l'expression régulière,
affiche l'indice du dernier caractère de la chaîne qui correspond à
l'expression régulière, sinon affiche -1.
- action=length
Affiche la longueur de la portion de la chaîne de caractères qui
correspond à l'expression régulière.
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.
- caseless=true
Avec cet attribut, la comparaison est faite indépendamment de la
casse des caractères.
Code source |
1:<char-offsets "abcdAbCdaBcD" a>
2:<char-offsets "abcdAbCdaBcD" a caseless=true>
|
Résultat |
1:0
8
2:0
4
8
|
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
|
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
|
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
|
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
|
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
|
- caseless=true
Avec cet attribut, la comparaison est faite indépendamment de la
casse des caractères.
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
|
- caseless=true
Avec cet attribut, la comparaison est faite indépendamment de la
casse des caractères.
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
|
- start=début
Indique le point de départ à partir duquel le décalage s'opère.
Code source |
<array-shift chiffres -2 start=2><get-var chiffres>
|
Résultat |
2
3
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
|
- caseless=true
Avec cet attribut, le tri est fait indépendamment de la casse des
caractères.
- numeric=true
Effectue un tri en comparant les valeurs numériquement, et non
alphabétiquement.
Code source |
<sort chiffres numeric=true><get-var chiffres>
|
Résultat |
2
3
12
|
- sortorder=reverse
Effectue le tri en ordre inverse
Code source |
<sort chiffres numeric=true sortorder=reverse>;;;
<get-var chiffres>
|
Résultat |
12
3
2
|
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
|
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.
Retourne true si le premier argument est strictement
supérieur au second.
Retourne true si le premier argument est strictement
inférieur au second.
Retourne true si les deux arguments sont égaux.
Retourne true si les deux arguments sont
différents.
Opérateurs relationnels
Retourne true si la chaîne de caractères est vide, et
rien sinon.
Retourne le dernier argument si tous les arguments sont non
vides, sinon ne retourne rien.
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.
- separator=chaîne
Par défaut, les arguments sont accolés. Cet attribut permet de
définir le séparateur inséré entre les arguments.
Cette fonction affiche ses arguments sans les développer. Ils ne
seront ensuite plus jamais développés, à moins d'utiliser la
commande expand.
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.
Quand la chaîne de caractères passée en premier argument n'est
pas vide, le corps de cette fonction est exécuté.
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
|
- start=début
La boucle commence à l'indice indiqué par cet attribut.
Code source |
<set-var x="1\n2\n3\n4\n5\n6">
<foreach i x start=3><get-var i> </foreach>
|
Résultat |
4 5 6
|
- end=fin
La boucle termine à l'indice indiqué par cet attribut.
Code source |
<set-var x="1\n2\n3\n4\n5\n6">
<foreach i x end=3><get-var i> </foreach>
|
Résultat |
1 2 3
|
- step=pas
Lorsqu'un pas est spécifié, il représente l'incrément de l'index
dans la boucle (1 par défaut). Si le pas est négatif, la boucle est
inversée.
Code source |
<set-var x="1\n2\n3\n4\n5\n6">
<foreach i x step=2><get-var i> </foreach>
<foreach i x step=-2><get-var i> </foreach>
|
Résultat |
1 3 5
6 4 2
|
|
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
|
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
|
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.
- up=numéro
Avec cet attribut, on peut choisir le nombre de fonctions à
terminer. Par défaut, seule la fonction la plus interne est
terminée (c'est-à-dire up=1). Si une valeur négative est spécifiée,
toutes les fonctions actuelles sont terminées.
Affiche le message donné en arguments sur l'erreur standard.
|
|
exit |
[message=chaîne] |
[status=rc] |
|
Interrompt immédiatement le programme.
- message=chaîne
Afficher une information sur l'erreur standard, pour indiquer la
cause de l'arrêt du programme.
- status=rc
Change le code retourné par le programme (-1 par défaut).
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
|
Affiche true si le fichier dont le nom est passé en
argument est trouvé, et rien sinon.
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.
- alt=action
Si le fichier n'est pas trouvé, le texte alternatif est affiché.
Si cet attribut est absent et que le fichier n'est pas trouvé, le
programme s'arrête.
- verbatim=true
Avec cet attribut, le contenu du fichier est inclus sans être
modifié par mp4h.
Le corps de cette balise est supprimé.
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.
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 "&" "&">
<subst-in-var verb-body "<" "<">
<subst-in-var verb-body ">" ">">
<set-var body=%body>
<subst-in-var body "&" "&">
<subst-in-var body "<" "<">
<subst-in-var body ">" ">">
<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>
|
Cette commande prend le même argument que l'option
-d.
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é.
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.
Fonction inverse de la précédente.
Divers
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.
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.
Affiche le numéro de version de mp4h.
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
|
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
|
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
|
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
|
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:
- La séquence %name est remplacée par le nom de la
balise.
- Les attributs sont numérotés à partir de 0. Dans le texte de
remplacement, %0 sera remplacé par le 1er attribut,
%1 par le 2e attribut, etc. Comme le nombre d'attribut n'est
pas limité, les lettres %20 seront remplacés par le
vingt-et-unième argument, et pas par le 3e argument suivi d'un 0.
Code source |
<define-tag href>
<a href="%0">%1</a>
</define-tag>
<href http://www.gimp.org "The Gimp">
|
Résultat |
<a href="http://www.gimp.org">The Gimp</a>
|
- La séquence %# affiche le nombre d'attributs passés à
la commande.
- La séquence %% est remplacée par un %, ce qui
est utile pour des définitions imbriquées.
Code source |
<define-tag externe>;;;
Externe, nombre d'attributs: %#
<define-tag interne1>;;;
Interne1, nombre d'attributs: %#;;;
</define-tag>;;;
<define-tag interne2>;;;
Interne2, nombre d'attributs: %%#;;;
</define-tag>;;;
<interne1 %attributes et quelques autres>
<interne2 %attributes et quelques autres>
</define-tag>
<externe liste attributs>
|
Résultat |
Externe, nombre d'attributs: 2
Interne1, nombre d'attributs: 2
Interne2, nombre d'attributs: 5
|
- Les caractères %attributes sont remplacés par la liste
des arguments, séparés par un espace.
Code source |
<define-tag mail1>
<set-var %attributes>
<get-var name>
<get-var mail>
</define-tag>
<set-var name="" mail="">
<mail1 name="Dr. Foo" mail="hello@foo.com">
|
Résultat |
Dr. Foo
hello@foo.com
|
- Les caractères %body sont remplacés par le corps de la
commande, dans le cas d'une balise complexe.
Code source |
<define-tag mail2 endtag=required whitespace=delete>
<set-var %attributes>
<a href="mailto:<get-var mail>">%body</a>
</define-tag>
<mail2 mail="hello@foo.com">
<img src="photo.png" alt="Dr. Foo" border=0>
</mail2>
|
Résultat |
<a href="mailto:hello@foo.com">
<img src="photo.png" alt="Dr. Foo" border=0>
</a>
|
- Les deux formes ci-dessus possèdent une forme alternative, où
la liste est séparée par des sauts de lignes au lieu d'espaces. Il
s'agit des formes %Aattributes et %Abody, le
A voulant dire array (tableau en français).
Code source |
<define-tag show-attributes>
<set-var list="%Aattributes" i=0>
<foreach attr list>
%<get-var i>: <get-var attr>
<increment i>
</foreach>
</define-tag>
<show-attributes name="Dr. Foo" mail="hello@foo.com">
|
Résultat |
%0: name=Dr. Foo
%1: mail=hello@foo.com
|
- Une autre forme est obtenue en remoplaçant le A par
U, auquel cas le texte est remplacé mais n'est lui-même
pas évalué. Cela n'a de sens que si la commande a été déclarée avec
l'attribut attributes=verbatim, parce que dans le cas
contraire les arguments sont développés avant que ces substitutions
n'aient lieu.
Code source |
<define-tag show1>
Avant développement: %Uattributes
Après développement: %attributes
</define-tag>
<define-tag show2 attributes=verbatim>
Avant développement: %Uattributes
Après développement: %attributes
</define-tag>
<define-tag bar>et hop %attributes</define-tag>
<show1 <bar la boum>>
<show2 <bar la boum>>
|
Résultat |
Avant développement: et hop la boum
Après développement: et hop la boum
Avant développement: <bar la boum>
Après développement: et hop la boum
|
- Les modificateurs A et U peuvent être
combinés.
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:
- le nom de la commande ;
- s'il s'agit d'une balise simple ou complexe ;
- si les attributs sont développés ou non ;
- le type de fonction (primitive ou commande de l'utilisateur)
;
- pour les primitives, l'adresse de la routine correspondante
dans le code exécutable, et pour les commandes définies par
l'utilisateur, le texte de remplacement de la commande.
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.