From madore@news.ens.fr Path: eleves!not-for-mail From: madore@news.ens.fr (GroTeXdieck) Newsgroups: ens.forum.informatique.lang Subject: Programmes self-rep Date: 3 Jun 1999 22:50:39 GMT Lines: 50 Sender: madore@clipper.ens.fr Message-ID: <7j70rv$o84$1@clipper.ens.fr> NNTP-Posting-Host: clipper.ens.fr Mime-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 8bit X-Trace: clipper.ens.fr 928450239 24836 129.199.129.1 (3 Jun 1999 22:50:39 GMT) X-Complaints-To: forum@clipper.ens.fr NNTP-Posting-Date: 3 Jun 1999 22:50:39 GMT X-Newsreader: Flrn (0.4pre2 - 05/99) Xref: eleves ens.forum.informatique.lang:129 Pour animer un peu ce conti croupissant, je propose de jouer un peu avec les programmes self-rep dans différents langages de programmation. Rappelons qu'un programme self-rep c'est un programme qui affiche son propre listing. Pour faire simple, disons que le théorème du point fixe assure que tout langage de programmation Turing-complet (et possédant un dispositif de sortie) permet d'écrire un programme self-rep. Parfois c'est très facile (comme le 10 LIST du BASIC), parfois carrément trivial (le programme vide), mais en tout cas c'est toujours possible, et normalement il n'y a pas besoin de hack affreux (enfin, le langage peut être nacsze et alors ça être laid, voire effectivement hackesque s'il y a des limitations bizarres sur la sortie par exemple). L'idée théorique c'est que si la fonction « quine » affiche son argument suivi de son argument quoté comme un appel de fonction (i.e. « quine('double-bubble') » affiche « double-bubble('double-bubble') » pour reprendre l'exemple de Hofstadter) alors « quine('quine') » va faire ce qu'il faut. Ce qu'il faut retenir en tout cas c'est qu'une bonne partie du programme (le « quine » en l'occurrence) va se retrouver deux fois, une fois comme corps du programme et une fois comme donnée à utiliser pour réconstituer le listing. Les programme self-rep sont même cofinaux dans les programmes. Il en existe d'arbitrairement longs. On peut même, dans un certain sens, rendre tout programme self-rep, en lui faisant faire sa tâche habituelle, *puis* afficher son listing. On peut imaginer des choses plus baroques : un programme A dans un langage X qui écrit le listing d'un programme B dans un langage Y qui écrit à son tour le listing de A (ça c'est facile : souvenons-nous que B peut très bien se contenter d'une suite de printf()) ; ou n programmes dans n langages différents chacun desquels demande à l'utilisateur de choisir un des n, et imprime son listing (ça ça devrait être rigolo). Dans certains langages de programmation on dispose d'une fonction d'évaluation. Dans ce cas, on peut simplifier le programme self-rep en réutilisant la même chaîne pour l'exécuter et pour l'afficher. C'est amusant, mais c'est quand même de la gruge et ça ne rentre pas dans le cadre de la théorie générale. Cependant, comme j'ai composé des programmes self-rep pour m'amuser, je n'ai pas manqué de faire appel à cette feature. Je vais poster quelques exemples que j'ai composé ce soir, dans différents langages. Je vous invite à essayer les langages que j'ai omis (par exemple TeX, perl, caml, fortran). Il faut quand même essayer que le programme soit non-trivial (par exemple, « foobar » en TeX ou m4 est selfrep, mais il ne montre pas la méthode générale) et si possible assez élégant ou autrement digne d'intérêt. Il faut éviter les one-liners quand la ligne est ridiculement longue. From madore@news.ens.fr Path: eleves!not-for-mail From: madore@news.ens.fr (GroTeXdieck) Newsgroups: ens.forum.informatique.lang Subject: Re: Programmes self-rep Date: 3 Jun 1999 22:57:22 GMT Lines: 155 Sender: madore@clipper.ens.fr Message-ID: <7j718i$o84$3@clipper.ens.fr> References: <7j70rv$o84$1@clipper.ens.fr> NNTP-Posting-Host: clipper.ens.fr Mime-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 8bit X-Trace: clipper.ens.fr 928450642 24836 129.199.129.1 (3 Jun 1999 22:57:22 GMT) X-Complaints-To: forum@clipper.ens.fr NNTP-Posting-Date: 3 Jun 1999 22:57:22 GMT X-Newsreader: Flrn (0.4pre2 - 05/99) Xref: eleves ens.forum.informatique.lang:130 Bon, commençons par le C. On peut faire assez court et hackesque mais on a alors une longue ligne, ce qui est mal : ----- main(){char *s="main(){char *s=%c%s%c;printf(s,34,s,34,10);}%c";printf(s,34,s,34,10);} ----- Ça peut faire un .sig éventuellement (d'ailleurs il me semble avoir vu un truc du genre en .sig). Bon, mais il est aussi possible de faire un bon programme C correctement écrit et commenté, conforme à la norme ANSI, portable (pas d'hypothèse sur le jeu de caractères utilisé) et proprement formaté. C'est un peu long, c'est tout : ----- #include /* This program is a self-replicating program. In other words, it * will print its own listing. There are many such programs. This * one, however, was designed in order to be elegant, portable, and * devoid of dirty hacks. */ /* The following characters must be quoted in a string. */ const char quote = '"'; const char escape = '\\'; /* Strings respectively prepended and appended before a quoted * string. */ const char *qline1 = " \""; const char *qline2 = "\",\n"; void printquoted (const char *s) /* Quote print a character string. */ { int i; printf ("%s", qline1); for ( i=0 ; s[i] ; i++ ) { if ( ( s[i] == quote ) || ( s[i] == escape ) ) printf ("%c", escape); printf ("%c", s[i]); } printf ("%s", qline2); } void quine (const char *tab[], const char *middle) /* The Quine operation: this function prints the first part of * the tab board (up to NULL), then prints it again quoted, the * prints the middle part, then the second part quoted, and * finally the second part unquoted. */ { int i,j; for ( i=0 ; tab[i] ; i++ ) printf ("%s\n", tab[i]); for ( j=0 ; tab[j] ; j++ ) printquoted (tab[j]); printf ("%s\n", middle); for ( j++ ; tab[j] ; j++ ) printquoted (tab[j]); for ( i++ ; tab[i] ; i++) printf ("%s\n", tab[i]); } /* The string to be printed in the "middle", between the first and * second parts. */ const char *middle = " NULL, /* And now the \"second\" part */"; const char *tab[] = { "#include ", "", "/* This program is a self-replicating program. In other words, it", " * will print its own listing. There are many such programs. This", " * one, however, was designed in order to be elegant, portable, and", " * devoid of dirty hacks. */", "", "/* The following characters must be quoted in a string. */", "const char quote = '\"';", "const char escape = '\\\\';", "", "/* Strings respectively prepended and appended before a quoted", " * string. */", "const char *qline1 = \" \\\"\";", "const char *qline2 = \"\\\",\\n\";", "", "void", "printquoted (const char *s)", " /* Quote print a character string. */", "{", " int i;", "", " printf (\"%s\", qline1);", " for ( i=0 ; s[i] ; i++ )", " {", " if ( ( s[i] == quote ) || ( s[i] == escape ) )", " printf (\"%c\", escape);", " printf (\"%c\", s[i]);", " }", " printf (\"%s\", qline2);", "}", "", "void", "quine (const char *tab[], const char *middle)", " /* The Quine operation: this function prints the first part of", " * the tab board (up to NULL), then prints it again quoted, the", " * prints the middle part, then the second part quoted, and", " * finally the second part unquoted. */", "{", " int i,j;", "", " for ( i=0 ; tab[i] ; i++ )", " printf (\"%s\\n\", tab[i]);", " for ( j=0 ; tab[j] ; j++ )", " printquoted (tab[j]);", " printf (\"%s\\n\", middle);", " for ( j++ ; tab[j] ; j++ )", " printquoted (tab[j]);", " for ( i++ ; tab[i] ; i++)", " printf (\"%s\\n\", tab[i]);", "}", "", "/* The string to be printed in the \"middle\", between the first and", " * second parts. */", "const char *middle = \" NULL, /* And now the \\\"second\\\" part */\";", "", "const char *tab[] = {", NULL, /* And now the "second" part */ " NULL", "};", "", "int", "main (void)", "{", " quine (tab, middle);", " return 0;", "}", NULL }; int main (void) { quine (tab, middle); return 0; } ----- Pas grand chose à dire, sinon que le principe « quine » a été coupé en deux, et ça donne quelque chose comme « qui('qui','ne')ne », et du coup il faut introduire un middle string (« ',' » dans cette analogie, voir la variable « middle » pour la valeur réelle). From madore@news.ens.fr Path: eleves!not-for-mail From: madore@news.ens.fr (GroTeXdieck) Newsgroups: ens.forum.informatique.lang Subject: Re: Programmes self-rep Date: 3 Jun 1999 23:00:08 GMT Lines: 127 Sender: madore@clipper.ens.fr Message-ID: <7j71do$ps7$1@clipper.ens.fr> References: <7j70rv$o84$1@clipper.ens.fr> NNTP-Posting-Host: clipper.ens.fr Mime-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 8bit X-Trace: clipper.ens.fr 928450809 26503 129.199.129.1 (3 Jun 1999 23:00:08 GMT) X-Complaints-To: forum@clipper.ens.fr NNTP-Posting-Date: 3 Jun 1999 23:00:08 GMT X-Newsreader: Flrn (0.4pre2 - 05/99) Xref: eleves ens.forum.informatique.lang:131 Le programme Java que j'ai pondu ressemble beaucoup à mon programme C. Il n'est pas commenté et il est un peu pourri (je ne connais presque pas le Java) mais sinon il est très semblable. ----- public class SelfRep { static final char quote = '"'; static final char escape = '\\'; public static String quote (String s) { int i; StringBuffer buf = new StringBuffer (); for ( i=0 ; i References: <7j70rv$o84$1@clipper.ens.fr> NNTP-Posting-Host: clipper.ens.fr Mime-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 8bit X-Trace: clipper.ens.fr 928451406 26503 129.199.129.1 (3 Jun 1999 23:10:06 GMT) X-Complaints-To: forum@clipper.ens.fr NNTP-Posting-Date: 3 Jun 1999 23:10:06 GMT X-Newsreader: Flrn (0.4pre2 - 05/99) Xref: eleves ens.forum.informatique.lang:132 En shell, on peut gruger carrément : ----- #! /bin/sh cat $0 ----- (on pouvait gruger comme ça en C, sauf que argv[0] renvoie le truc compilé, pas le source ;-). Pour faire un self-rep non trivial, il va falloir procéder comme en C. Au lieu de prendre un tableau, j'utilise un fichier temporaire (et j'en prends deux, un pour le predata et un pour le postdata ; ici il n'y a pas de meddata). La commande sed permet de faire facilement les quotages (à vrai dire je m'en suis servi pour produire la partie médiane du programme parce que compter les backslashes ça devenait lourd). ----- #! /bin/sh PREFILE=`mktemp /tmp/selfrep.XXXXXX` || exit 1 POSTFILE=`mktemp /tmp/selfrep.XXXXXX` || exit 1 echo "#! /bin/sh" >>$PREFILE echo "PREFILE=\`mktemp /tmp/selfrep.XXXXXX\` || exit 1" >>$PREFILE echo "POSTFILE=\`mktemp /tmp/selfrep.XXXXXX\` || exit 1" >>$PREFILE echo "cat \$PREFILE" >>$POSTFILE echo "cat \$PREFILE | sed -e 's/\\([\`\"\$\\]\\)/\\\\\\1/g' | \\" >>$POSTFILE echo " sed -e 's/^/echo \"/' | sed -e 's/\$/\" >>\$PREFILE/'" >>$POSTFILE echo "cat \$POSTFILE | sed -e 's/\\([\`\"\$\\]\\)/\\\\\\1/g' | \\" >>$POSTFILE echo " sed -e 's/^/echo \"/' | sed -e 's/\$/\" >>\$POSTFILE/'" >>$POSTFILE echo "cat \$POSTFILE" >>$POSTFILE echo "rm \$PREFILE" >>$POSTFILE echo "rm \$POSTFILE" >>$POSTFILE cat $PREFILE cat $PREFILE | sed -e 's/\([`"$\]\)/\\\1/g' | \ sed -e 's/^/echo "/' | sed -e 's/$/" >>$PREFILE/' cat $POSTFILE | sed -e 's/\([`"$\]\)/\\\1/g' | \ sed -e 's/^/echo "/' | sed -e 's/$/" >>$POSTFILE/' cat $POSTFILE rm $PREFILE rm $POSTFILE ----- Par ailleurs, le shell possède une fonction eval, donc on peut s'en servir. L'ennui, c'est qu'apparemment /bin/sh ne tolère pas les retours chariot dans les noms de variable. Qu'à cela ne tienne. On va utiliser l'espace à la place (et se servir de for i in) ; comme on veut quand même pouvoir mettre des espaces dans les commandes, on va mettre des traits de soulignements à leur place, et utiliser \_ pour représenter _. Le nombre de backslashes devient carrément impressionnant par endroits... ----- #! /bin/sh x=" echo_\"#!_/bin/sh\" echo_\"x=\\\"\" for_u_in_\$x;_do_echo_\$u_|_sed_-e_'s/\\([\`\"\$\\]\\)/\\\\\\1/g';_done echo_\"\\\"\" echo_\"for_i_in_\\\$x;_do\" echo_\"eval_\\\`echo_\\\$i_|_sed_-e_'s/\\\\([^\\\\]\\\\)\\_/\\\\1_/g'_|_\\\\\" echo_\"sed_-e_'s/\\\\\\\\\\_/\\_/g'\\\`\" echo_\"done\" " for i in $x; do eval `echo $i | sed -e 's/\([^\]\)_/\1 /g' | \ sed -e 's/\\_/_/g'` done ----- Il faut imaginer que x est écrit dans un langage bizarre (qui ressemble beaucoup au shell mais avec des _ en lieu d'espaces). Mais dans ce langage on peut accéder au programme en cours (sous le nom de $x) donc on va réitérer la gruge initiale avec le $0. Il ne reste plus qu'à mettre ce programme dans une variable shell (x), et ne pas oublier d'écrire (dans les quatre dernières lignes) un interpréteur du langage bizarre, que le programme x doit aussi afficher (avec des echo). Exercice : expliquer le sens de chacun des backslashes là où il y en a 10 d'affilée. From madore@news.ens.fr Path: eleves!not-for-mail From: madore@news.ens.fr (GroTeXdieck) Newsgroups: ens.forum.informatique.lang Subject: Re: Programmes self-rep Date: 3 Jun 1999 23:19:15 GMT Lines: 42 Sender: madore@clipper.ens.fr Message-ID: <7j72hj$ps7$3@clipper.ens.fr> References: <7j70rv$o84$1@clipper.ens.fr> NNTP-Posting-Host: clipper.ens.fr Mime-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 8bit X-Trace: clipper.ens.fr 928451955 26503 129.199.129.1 (3 Jun 1999 23:19:15 GMT) X-Complaints-To: forum@clipper.ens.fr NNTP-Posting-Date: 3 Jun 1999 23:19:15 GMT X-Newsreader: Flrn (0.4pre2 - 05/99) Xref: eleves ens.forum.informatique.lang:133 En dc, un programme selfrep s'écrit très facilement : ----- [91PdP93PP10P]91PdP93PP10P ----- Il faut lire ça comme ceci : on prend la chaîne entre crochets, on la met sur la pile. Puis on prend 91 et on l'affiche (P), c'est-à-dire qu'on afficher un crochet ouvert ([ a le code ASCII 91). Puis on duplique (d) la chaîne qu'on avait mis sur la pile et on en affiche (P) une. Puis on affiche un crochet fermé (93P) puis la chaîne à nouveau (P - qui va chercher la copie qu'on avait mis sur la pile). Et enfin on affiche un retour chariot. Là, on a le « quine('quine') » dans toute sa splendide beauté, avec quine = 91PdP93PP10P, si ce n'est que c'est un langage à pile donc c'est [quine]quine. Comme dc a aussi une fonction d'évaluation (x), on peut l'utiliser pour faire légèrement plus concis : ----- [91PP93P[dx]P10P]dx ----- Autrement dit, on prend toute la chaîne (entre les crochets extérieurs), on la duplique (d) et on en exécute une copie (x). La chaîne en question, elle affiche un crochet ouvert (91P), puis elle s'affiche tout entière (P), suivie d'un crochet fermé (93P), de la chaîne « dx » ([dx]P) et enfin d'un retour chariot (10P). La technique est donc la même que dans l'exemple en shell mais je ne sais pas ce qui est le plus illisible. Sans autre but que de faire plus long et de passer par une variable, il y a aussi l'exemple suivant : ----- [91PlxP93P[sxlxx]P10P]sxlxx ----- qui utilise une variable x pour stocker la chaîne plutôt que de la mettre sur la pile (ça ressemble donc encore plus à mon exemple en shell). From madore@news.ens.fr Path: eleves!not-for-mail From: madore@news.ens.fr (GroTeXdieck) Newsgroups: ens.forum.informatique.lang Subject: Re: Programmes self-rep Date: 3 Jun 1999 23:25:55 GMT Lines: 21 Sender: madore@clipper.ens.fr Message-ID: <7j72u3$ps7$4@clipper.ens.fr> References: <7j70rv$o84$1@clipper.ens.fr> NNTP-Posting-Host: clipper.ens.fr Mime-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 8bit X-Trace: clipper.ens.fr 928452355 26503 129.199.129.1 (3 Jun 1999 23:25:55 GMT) X-Complaints-To: forum@clipper.ens.fr NNTP-Posting-Date: 3 Jun 1999 23:25:55 GMT X-Newsreader: Flrn (0.4pre2 - 05/99) Xref: eleves ens.forum.informatique.lang:134 Comme j'en suis l'inventeur, il faut bien que je parle du SIMPLE. Mon programme self-rep en SIMPLE il donne ceci : ----- `|% @,2@]>% ```|`% ``@,2`@`]`>`% ``% >% ----- En SIMPLE on a le méchanisme de quotation tout prêt (c'est la fonction inputform). On définit la fonction quine, qui va renvoyer son premier argument (@,1@ ; la virgule signifie sans évaluation, mais il y a toujours une évaluation de plus qu'on oublie) suivi de son premier argument quoté, puis le caractère « | », puis son second argument quoté, et enfin son second argument non quoté. Autrement dit, les deux arguments de quine sont le predata et le postdata de mes exemples précédents. Ensuite, j'appelle quine avec les deux arguments Qui Vont Bien (quotés comme le fera la fonction inputform). From madore@news.ens.fr Path: eleves!not-for-mail From: madore@news.ens.fr (GroTeXdieck) Newsgroups: ens.forum.informatique.lang Subject: Re: Programmes self-rep Date: 3 Jun 1999 23:49:40 GMT Lines: 89 Sender: madore@clipper.ens.fr Message-ID: <7j74ak$ps7$5@clipper.ens.fr> References: <7j70rv$o84$1@clipper.ens.fr> NNTP-Posting-Host: clipper.ens.fr Mime-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 8bit X-Trace: clipper.ens.fr 928453780 26503 129.199.129.1 (3 Jun 1999 23:49:40 GMT) X-Complaints-To: forum@clipper.ens.fr NNTP-Posting-Date: 3 Jun 1999 23:49:40 GMT X-Newsreader: Flrn (0.4pre2 - 05/99) Xref: eleves ens.forum.informatique.lang:135 Passons au langage béni par les dieux : le Scheme. Le Scheme a l'avantage (un peu comme le SIMPLE) d'avoir une fonction write qui imprime une sexp sous forme d'entrée. Ça va nous permettre d'éviter les gymnastiques avec les chaînes de caractères qu'on a fait dans tous les langages jusqu'à maintenant. Si on avait défini quine comme il faut, (quine 'quine) afficherait (quine 'quine). Comment faut-il le définir comme il faut ? Eh bien on veut que (quote x) appelle write avec x et 'x comme arguments. Là, il s'agit de quoter la valeur de x et cela se fait avec quasiquote, précisément (write `(,x ',x)), ce qui signifie appeler write avec [` = sans évaluation] ([, = ben si, on évalue : valeur de]x, [' = vraiment pas d'évaluation][, = valeur de] x). Bref, si on fait (define (quine x) (write `(,x ',x))) alors (quine 'quine) va afficher (quine (quote quine)), ce qui est presque bon à part que le ' a été remplacé par sa désabréviation qui est quote. On peut donc faire (define (quine x) (write (quasiquote ((unquote x) (quote (unquote x)))))) puis (quine (quote quine)). Bon, sauf que dans tout ça la définition de quine n'est pas affichée. Si on veut l'afficher, on la supprime, i.e. on remplace quine par sa définition dans l'expression (quine (quote quine)) et on tombe sur l'horrible one-liner suivant : ----- ((lambda (x) (write (quasiquote ((unquote x) (quote (unquote x)))))) (quote (lambda (x) (write (quasiquote ((unquote x) (quote (unquote x)))))))) ----- Si vous êtes perdu dans les niveaux de quote, on peut aussi définir quine de manière plus compréhensible par (define (quine x) (write (list x (list 'quote x)))) ce qui est exactement la même chose que le truc précédent. Le one-liner devient alors ----- ((lambda (x) (write (list x (list (quote quote) x)))) (quote (lambda (x) (write (list x (list (quote quote) x)))))) ----- (lequel est le plus atroce à votre avis ?). Bon, mais ça c'était pour la théorie. Ces trucs n'affichent même pas de retour chariot à la fin donc c'est MAL. Si on veut un vrai selfrep, on fait comme tout le monde, on définit une fonction (do) qui prend une liste de sexps et qui les affiche, affiche un truc intermédiaire, réaffiche les mêmes sexps, puis affiche un truc final. Grâce à la manière dont les quotations se font en scheme, on n'a pas besoin de mécanisme complexe de quotation (juste un ' devant la liste) ; à part ça il faut faire attention à mettre précisément une sexp par ligne. Ça donne le petit bijou suivant : ----- (define (line-write x) (write x) (newline)) (define (d l) (map line-write l)) (define (mid) (display "(do '(") (newline)) (define (end) (display "))") (newline)) (define (do l) (d l) (mid) (d l) (end)) (do '( (define (line-write x) (write x) (newline)) (define (d l) (map line-write l)) (define (mid) (display "(do '(") (newline)) (define (end) (display "))") (newline)) (define (do l) (d l) (mid) (d l) (end)) )) ----- (Mais là j'ai été obligé de recourir à display alors que les deux one-liners qui précédaient avaient la beauté de faire seulement du write.) Le scheme (certains schemes du moins) a une fonction eval. On peut donc s'en servir comme on a fait dans les précédents langages à eval (le shell, dc). C'est quand même plus beau en scheme : ----- (define x '( (display "(define x '(") (newline) (map (lambda (s) (write s) (newline)) x) (display "))") (newline) (display "(map eval x)") (newline) )) (map eval x) ----- La fonction map applique son deuxième argument à tous les éléments de son troisième argument (une liste). On définit donc un petit programme x, succession de sexps. Pour l'exécuter, on fait (map eval x). Pour l'afficher, à raison d'une sexp par ligne, on mappe (lambda (s) (write s) (newline)) à x. Le reste est de routine. From madore@news.ens.fr Path: eleves!not-for-mail From: madore@news.ens.fr (GroTeXdieck) Newsgroups: ens.forum.informatique.lang Subject: Re: Programmes self-rep Date: 3 Jun 1999 23:55:38 GMT Lines: 75 Sender: madore@clipper.ens.fr Message-ID: <7j74lq$ps7$6@clipper.ens.fr> References: <7j70rv$o84$1@clipper.ens.fr> NNTP-Posting-Host: clipper.ens.fr Mime-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 8bit X-Trace: clipper.ens.fr 928454138 26503 129.199.129.1 (3 Jun 1999 23:55:38 GMT) X-Complaints-To: forum@clipper.ens.fr NNTP-Posting-Date: 3 Jun 1999 23:55:38 GMT X-Newsreader: Flrn (0.4pre2 - 05/99) Xref: eleves ens.forum.informatique.lang:136 Je termine mes exemples avec le PostScript. Là, il s'agit de dessiner son propre listing. Ça n'a rien de vraiment compliqué. Je suis l'exemple de mon programme Java. Comme manipuler des chaîne en PostScript est quand même lourd, j'utilise le fait que des parenthèses correctement emboîtées n'ont pas besoin d'être quotées dans une chaîne (les chaînes de caractères sont délimitées par des parenthèses en PostScript). J'évite donc complètement l'usage du backslash. Comme j'ai besoin d'une parenthèse ouvrante seule et d'une parenthèse fermante seule, je les obtiens comme sous-chaînes de la chaîne « () ». C'est un peu de la gruge mais ça simplifie quand même beaucoup les choses. Bref, on a : ----- %! /size 10 def /y 825 def /x0 15 def /Courier size selectfont /newln { /y y size sub def x0 y moveto } bind def newln /lead-and-trail ( ()) def /lead lead-and-trail 0 3 getinterval def /trail lead-and-trail 3 1 getinterval def /quine { /med exch def /post exch def /pre exch def pre { show newln } forall pre { lead show show trail show newln } forall med { show newln } forall post { lead show show trail show newln } forall post { show newln } forall } bind def [ (%!) (/size 10 def /y 825 def /x0 15 def) (/Courier size selectfont) (/newln {) ( /y y size sub def) ( x0 y moveto) (} bind def) (newln) (/lead-and-trail ( ()) def) (/lead lead-and-trail 0 3 getinterval def) (/trail lead-and-trail 3 1 getinterval def) (/quine {) ( /med exch def /post exch def /pre exch def) ( pre { show newln } forall pre { lead show show trail show newln } forall) ( med { show newln } forall) ( post { lead show show trail show newln } forall post { show newln } forall) (} bind def) ([) ] [ (]) ([) ( (])) ( ([)) (]) (quine) (showpage) ] [ (]) ([) ] quine showpage ----- Pour le reste c'est tout comme mon programme Java. Les tableaux sont délimités par des crochets. Je passe le prédata, le postdata et le meddata dans cet ordre sur la pile et j'appelle quine qui affiche le prédata, puis le prédata avec des parenthèses autour, puis le meddata, puis le postdata avec parenthèses, et enfin le postdata tout seul. Bien sûr, si on omet le %! au début, le programme affiche encore son propre listing, et pour cause... ;-)