On présente plusieurs exemples d'"opérateurs" dont le comportement est lié au contexte dans lequel on les utilise – il s'agit soit d'opérateurs du langage sont de textes qu'on aura identifiés comme représentant des concepts significatifs du langage.
Les opérateurs du langage sont définis comme des textes placés dans l'« environnement global » ; la prise en compte d'information de contexte se traduit donc simplement par la redéfinition de l'opérateur concerné dans un environnement local.
L'exemple classique d'un opérateur dont le schéma de décompilation dépend du contexte est celui du « else pendant » de Pascal. On définit ici les opérateurs de phrase conditionnelle suivants :
opérateur cond1
(def cond1 ()
("if " (TEST) " then" "^M"
" " (ALORS) "^M"
(lsp
(if (egal (use SINON) "")
(rep "else")
(rep "else" "^M"
" " (SINON))))))
On affiche systématiquement le mot-clé "else", qu'il y ait ou non une « partie SINON ».
opérateur cond2
(def cond2 ()
("if " (TEST) " then" "^M"
(lsp
(if (egal (use SINON) "")
(rep " " (ALORS))
(rep " " (ALORS
((def cond () ((cond1))))) "^M"
"else" "^M"
" " (SINON))))))
Dans cond2 :
SINON » est vide : on affiche simplement la « partie ALORS » (en particulier on n'affiche pas de "else") ;ALORS », en redéfinissant l'opérateur cond sur l'opérateur cond1,SINON » simplement.opérateur cond
(def cond ()
((cond2)))
L'opérateur cond est l'opérateur utilisé dans l'arbre syntaxique du programme :
cond2 : si la « partie SINON » est vide, on ne veut pas voir s'afficher un "else" inutile ;SINON » conserve la propriété de ne pas afficher les "else" inutiles ; en revanche, la « partie ALORS », qui redéfinit l'opérateur cond sur cond1, va forcer cet affichage.Par exemple :
On n'affiche aucun "else" :
| forme « textuelle » | forme « visuelle » |
| |
Sur la « branche ALORS », l'existence d'une « partie SINON » force l'affichage du "else" (la règle de syntaxe de Pascal rapporte la « partie SINON » introduite par "else" à la phrase conditionnelle la plus proche pour laquelle ne se rapporte encore aucune « partie SINON ») :
| |
Dans la « partie SINON de la partie ALORS » il n'y a pas nécessité d'affichage du "else" :
| |
Dans cette même partie on affiche le "else" : cette propriété est "héritée" sur la « branche SINON » du deuxième niveau de la « branche ALORS » du premier niveau :
| |
Il ne faut cependant pas oublier de redéfinir l'opérateur cond par sa forme initiale (sur cond2) dans une sous-branche où l'ambiguïté disparaît. Par exemple, l'opérateur repeat :
(def repeat ()
("repeat" "^M"
" " (INSTR
((def cond () ((cond2))))) "^M"
"until " (EXP-COND)))
Le parenthésage implicite défini par l'opérateur repeat fait "oublier" le contexte d'apparition de cet opérateur.
On peut aussi avoir des ambiguïtés d'affichage introduites à la définition d'"opérateurs" propres à l'utilisateur. Par exemple, on définit l'"opérateur" :
(def lecture ()
("carcou := carlu;" "^M"
"read(carlu);"))
qui est utilisé dans le programme comme une instruction indivisible.
Selon la position de l'"opérateur" dans le programme, l'affichage sera différent. Par exemple :
[1] begin
...
carcou := carlu;
read(carlu);
Traitement(carcou);
...
end;
[2] while condition do
begin
carcou := carlu;
read(carlu);
end;
dans le cas [1], les deux instructions sont affichées "en ligne" ; dans le cas [2] on les encadre par les parenthèses begin-end.
On peut systématiquement se placer dans le cas [2], mais ceci risque d'entraver une bonne lisibilité du programme. Pour conserver les deux formes possibles d'affichage on introduit alors un opérateur de groupement d'instructions qui se chargera de la prise en compte du contexte d'utilisation de l'opérateur.
(def list-instr1 ()
("begin" "^M"
" " (lst-init ((INSTR*))
((INSTR) ";" "^M"))
"end;" "^M"))
(def list-instr2 ()
((lst-init ((INSTR*))
((INSTR) ";" "^M"))))
(def list-instr ()
((lst-instr2)))
L'"opérateur" liste-instr est défini par défaut sur liste-instr2 : on n'affiche pas les parenthèses begin-end. Certains opérateurs redéfinissent alors liste-instr. Par exemple :
(def while ()
("while " (EXP-COND) " do" "^M"
" " (INSTR
((def liste-instr () ((liste-instr1)))))))
L'"opérateur" lecture est défini sur liste-instr : il laisse à ce dernier le soin de décider l'affichage ou non des parenthèses begin-end.
(def lecture ()
((def INSTR*
(lst-env
((def INSTR () ("carcou := carlu")))
(def INSTR () ("read(carlu)"))))))
((liste-instr)))