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.
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.
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; }