English version

Maple (page écrite par Vincent Lefèvre)

Utiliser Maple comme filtre, avec Perl

Cette section décrit comment utiliser Maple avec le langage Perl sous Unix (Linux ou Solaris).

J'ai finalement écrit un petit module Perl qui fournit une interface Perl à Maple: Maple.pm. Il n'y a pas encore de documentation, exceptés les commentaires dans ce module; le code lui-même est facile à comprendre et peut donner plus d'information.

Ce module supporte deux modes pour la communication interprocessus: pipe (pour utiliser un pipe, via IPC::Open2) et pty (pour utiliser un pseudo-terminal, via IPC::Run). Ces deux modes sont décrits ci-dessous; attention, c'est plutôt technique! Le mode pipe est le mode par défaut, parce qu'il est plus fiable et utilise un module qui devrait déjà être installé partout; mais il ne fonctionne pas avec Maple 9.5.

Avec IPC::Open2 (en utilisant des pipes)

C'est la solution la plus simple (la plus propre?), mais elle ne fonctionne pas avec Maple 9.5.

Maple peut être lancé avec:

require IPC::Open2;
IPC::Open2::open2(\*RD, \*WR, "maple -q");
print WR "interface(prettyprint=0,screenwidth=infinity):\n";

Note: au lieu de require, on peut aussi utiliser use pour que certains symboles soient exportés, mais require permet de ne charger le module (et ainsi de demander sa présence) que s'il est utilisé, et ceci peut être préférable quand on a le choix entre plusieurs modules, comme celui-ci et celui présenté dans la section suivante.

Ensuite on peut envoyer des données à Maple en écrivant dans le file handle WR et on peut lire des données en retour avec <RD>, comme pour n'importe quel fichier ou pipe. La ligne print ci-dessus permet de s'assurer que les résultats tiendront sur une seule ligne. Grâce à la fonction Perl alarm, on peut donner un timeout (voir les pages man de Perl). Pour quitter Maple proprement, il suffit juste de fermer les deux file handles (et éventuellement vérifier les valeurs de retour de close pour les erreurs possibles).

Cette méthode fonctionne bien jusqu'à Maple 9, mais malheureusement, Maple 9.5 bufferise complètement son flux de sortie quand il n'est pas attaché à un terminal, et il ne semble y avoir aucun moyen de changer ce comportement. Ceci signifie que si on a besoin d'envoyer à Maple des données basées sur un résultat antérieur de Maple, cette méthode n'est plus possible. D'où la méthode expliquée dans la section suivante.

Avec IPC::Run (en utilisant un pseudo-terminal)

On doit utiliser cette méthode avec Maple 9.5 si les entrées/sorties doivent être imbriquées, car la sortie est complètement bufferisée à moins qu'elle ne corresponde à un terminal. Ainsi, le but de cette méthode est d'utiliser un pseudo-terminal (pty) pour pouvoir récupérer les résultats immédiatement.

Maple peut être lancé avec:

require IPC::Run;
my ($pty,$ptyin,$ptyout);
my @maple = qw(maple -q);
$pty = IPC::Run::start(\@maple, '<pty<', \$ptyin, '1>pty>', \$ptyout);

La page man de IPC::Run décrit comment lire et écrire dans le pty. Mais cette page man dit aussi:

Sending to stdin will cause an echo on stdout, which occurs before each line is passed to the child program. There is currently no way to disable this, although the child process can and should disable it for things like passwords.

En gros, l'entrée se retrouve aussi sur la sortie, mais avec Maple, c'est pire, car l'entrée peut aléatoirement se retrouver en simple ou en double sur la sortie (un bug?), comme cela peut se voir avec ce script Perl, qui se bloque à une itération arbitraire. Il arrive même dans certains cas que les différentes lignes se retrouvent dans le désordre! La solution est d'écrire l'entrée sur une seule ligne et d'utiliser des marqueurs à la fois au début et en fin d'entrée. Avec Maple, on peut choisir un nom inutilisé suivi d'un deux-points. Ainsi, on peut utiliser la routine suivante pour envoyer des données à Maple et lire le résultat en retour:

sub maple_eval
  {
    my $in = "@_";
    $in =~ tr/\n/ /;
    $ptyin = "StartOfInput: $in EndOfInput:\n";
    pump $pty while length $ptyin ||
      ($ptyout =~ s/\s*StartOfInput:.*?EndOfInput:\s*//gs,
       $ptyout =~ /StartOfInput:/s || $ptyout !~ s/^\s*(\S.*?)\s*\n//);
    return $1;
  }


webmaster@vinc17.org