Rappelons d'abord qu'IndexMatic ne reconnaît que les expressions régulières ExtendScript, un dialecte qui prend en charge les assertions « vers l'avant », dites LOOKAHEAD positif ou négatif, usant de la forme X(?=Y) ou X(?!Y). Mais il restera de marbre devant les schémas « vers l'arrière » de type LOOKBEHIND, formalisés respectivement (?<=Y)X et (?<!Y)X en syntaxe Grep.

Il n'est pas rare cependant que les éléments préfixes d'une expression décident de sa pertinence pour votre index. Dans de nombreux cas, par exemple « Premier ministre » versus « ministre », il suffira de capturer l'expression complète /Premier ministre/ pour la discriminer ipso facto de sa forme indésirable. Puisque c'est bien le terme « Premier ministre » que vous souhaitez indexer, aucune difficulté ne se fait jour. Le problème du LOOKBEHIND ne se pose concrètement que si vous devez capturer une certaine forme à condition qu'elle possède tel préfixe mais sans capturer ledit préfixe.

Lookbehind positif

Supposez maintenant que vous produisiez l'index d'un catalogue de produits. Mettons que chaque nom de produit susceptible d'indexation soit systématiquement précédé d'un code à trois chiffres suivi d'une barre verticale, par ex. 123|étagère, et qu'il n'y ait pas d'autre moyen formel d'extraire l'élément cible. Dans pareille situation, le préfixe \d\d\d\| devient la condition d'extraction du terme étagère, sauf que ce code ne doit pas apparaître dans l'index.

En GREP, il n'y aurait qu'à envoyer une commande de la forme (?<=\d\d\d\|)\w+ pour extraire le nom d'un produit se trouvant dans le sillage de n'importe quel code. Mais cela échouerait dans ExtendScript.

Cependant, IndexMatic peut contourner le problème. Il suffit en effet de capturer le motif tout entier et de sélectionner en sortie le segment qui nous intéresse :

   /(\d\d\d\|)(\w+)/ => $2

Le schéma général d'un lookbehind simulé est donc (PRÉFIXE)(TERME) => $2, PRÉFIXE représentant la condition en amont, TERME représentant le motif à extraire. Grâce aux parenthèses capturantes, la règle => $2 ignore l'élément préfixe $1 et ne produit finalement que le TERME voulu $2.

Note. — Cette astuce comporte un inconvénient subtil mais fâcheux : IndexMatic indique toujours le numéro de page associé à la capture tout entière. Si par accident l'élément PRÉFIXE se trouve en page 2 alors que TERME le suit en page 3, l'index final indiquera TERME en page 2, car c'est en effet la position où débute la concordance /(PRÉFIXE)(TERME)/.

Dans cette situation particulière, la requête /(préfixe )terme/=>$2 indexe le mot «terme» en page 2 !

Lookbehind négatif

Nous cherchons désormais à capturer un terme pour autant qu'il ne soit pas précédé par un certain motif. Problème en apparence antipodique, voire un poil plus retors puisque nous ne pouvons pas exprimer « positivement » un préfixe valide. Pourtant, il reste étonnamment facile de tamiser les candidats pour ne retenir que la forme autorisée, par l'astuce schématisée ci-dessous :

    (PRÉFIXE TERME)|(TERME) => $2

Cette fois, la première parenthèse (PRÉFIXE TERME) matérialise la forme que nous voulons exclure, tandis que la seconde parenthèse, (TERME) seul, représente ce que nous tolérons. Un petit tour de magie s'opère alors. Du fait de l'alternative, la variable $1 est assignée à chaque fois que PRÉFIXE TERME est trouvé dans le texte, alors que $2 ne recevra un TERME que dans le cas contraire. Mais puisque la requête expose $2 et jamais $1, tous les contextes qui satisfont PRÉFIXE TERME sont finalement ignorés. Vous obtenez donc l'effet exact d'un lookbehind négatif.

À titre d'illustration, renversons la contrainte de l'exemple examiné plus haut. Le but est maintenant d'indexer les expressions de la forme \w+ sauf celles préfixées par un code \d\d\d\|. Il suffit d'adresser à IndexMatic la requête suivante :

   /(\d\d\d\|\w+)|(\w+)/ => $2

Notez en passant que le schème /(X)|(Y)/ => $2 a d'innombrables applications. Mettons que votre objectif soit d'identifier toutes les occurrences du mot instance, sauf celles composées entre guillemets. Il suffit là encore de capturer les deux formes concurrentes — la plus longue devant apparaître en premier — puis d'exposer le terme $2 :

    /(["«] ?instance ?["»])|(instance)/ => $2

La technique est la même : on fait digérer la forme indésirable par le premier bloc afin que le second ne puisse plus l'enregistrer.