Version française

Maple (Page Written by Vincent Lefèvre)

Using Maple as a Filter, With Perl

This section describes how to use Maple with the Perl language under Unix (Linux or Solaris).

I've finally written a small Perl module that provides a Perl interface to Maple: Maple.pm. No documentation is provided yet, except the comments in this module; the code itself is easy to understand and may give more information.

This module supports two modes for interprocess communication: pipe (to use a pipe, via IPC::Open2) and pty (to use a pseudo-terminal, via IPC::Run). Both modes are described below; beware, this is rather technical! The mode pipe is the default, because it is more reliable and uses a module that should already be installed everywhere; but it does not work with Maple 9.5.

With IPC::Open2 (Using Pipes)

This is the easiest (cleanest?) solution, but it does not work with Maple 9.5.

Maple can be started with:

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

Note: Instead of require, one can also use use so that some symbols are exported, but require allows to load the module (thus require it to be present) only if it is used, and this may be better when one has the choice between several modules, like this one and the one presented in the next section.

Then one can send data to Maple by writing to the WR file handle and one can read back data with <RD>, like for any file or pipe. The above print line allows to make sure the results will fit on one line. Thanks to the alarm Perl function, a timeout can be given (see the Perl man pages). To quit Maple in a clean way, just close both file handles (and possibly check the close return values for any error).

This method works well until Maple 9, but unfortunately, Maple 9.5 fully buffers its output when it is not attached to a terminal, and there seems to be no way to change this behavior. This means that if one needs to send data to Maple based on an earlier result from Maple, this method is no longer possible. Hence the one explained in the next section.

With IPC::Run (Using a Pseudo-Terminal)

One must use this method with Maple 9.5 if the intput/output need to be interlaced, as the output is fully buffered unless it corresponds to a terminal. So, the goal of this method is to use a pseudo-terminal (pty) to be able to retrieve the results immediately.

Maple can be started with:

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

How to read from and write to the pty is described in the IPC::Run man page. But this man page also says:

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.

And with Maple, this is even worse since the input can be randomly echoed once or twice (a bug?), as shown by this Perl script, which freezes at a random iteration. In some cases, one can even get the different lines out of order! The solution is to write the input on a single line and to use markers both at the beginning and at the end of the input. With Maple, one can choose an unused name followed by a colon. Thus, one can use the following routine to write data to Maple and read back the result:

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