Inline-Octave
view release on metacpan or search on metacpan
#
# now, when perl6 comes out ...
#
sub bind_octave_function
{
my $o= shift;
my $oct_funname = shift;
my $perl_funname = shift;
my $nargout = shift;
my $pkg= $o->{API}->{pkg};
my $code = <<CODE;
package $pkg;
sub $perl_funname {
# we need to prevent IOM variables from going out of scope
# in the loop, but rather at the end of the function
#input variables
my \$inargs=" ";
my \@vin;
for (my \$i=0; \$i < \@_; \$i++) {
\$vin[\$i]= Inline::Octave->new( \$_[\$i] );
\$inargs.= \$vin[\$i]->name.",";
}
chop(\$inargs); #remove last ,
#output variables
my \$outargs=" ";
my \@vout;
for (my \$i=0; \$i < $nargout; \$i++) {
\$vout[\$i]= Inline::Octave->new( $retcode_string );
\$outargs.= \$vout[\$i]->name.",";
}
chop(\$outargs); #remove last ,
\$outargs= "[".\$outargs."]=";
\$outargs= "" if $nargout==0;
my \$call= "\$outargs $oct_funname(\$inargs);";
# print "--\$call--\\n";
my \$retval= Inline::Octave::interpret(0, \$call );
# print "--\$retval--\\n";
# Get the correct size for each new variable
foreach (\@vout) { \$_->store_size(); }
return \@vout if wantarray();
return \$vout[0];
}
CODE
#print "--$code--\n";
eval $code;
croak "Problem binding $oct_funname to $perl_funname: $@" if $@;
$octave_object->{FUNCS}->{$oct_funname}= $perl_funname;
return;
}
sub start_interpreter
{
my $o = shift;
# check if interpreter already alive
return if $octave_object->{OCTIN} and $octave_object->{OCTOUT};
use IPC::Open3;
use IO::File;
my $Oin = new IO::File;
my $Oout= new IO::File;
my $Oerr= new IO::File;
my $pid;
eval {
$pid= open3( $Oin , $Oout, $Oerr, $octave_object->{INTERP} );
# set our priority lower than the kid, so that we don't read each
# character. Experimentally, I've found 3 to be optimum on Linux 2.4.21
setpriority 0,0, (getpriority 0,0)+10;
};
# ignore errors from setpriority if it's not available
croak "Can't locate octave interpreter: $@\n" if $@ =~ /Open3/i;
my $select= IO::Select->new($Oout, $Oerr);
# New idea - start octave with
# use IPC::Run qw(start);
# my ($Oin, $Oout, $Oerr);
# my $pid;
# eval {
# $pid= start $octave_object->{INTERP}, \$Oin, \$Oout, \$Oerr
# };
# croak "Error starting octave interpreter: $@\n" if $@;
$octave_object->{octave_pid} = $pid;
$octave_object->{OCTIN} = $Oin;
$octave_object->{OCTOUT} = $Oout;
$octave_object->{OCTERR} = $Oerr;
$octave_object->{SELECT} = $select;
# some of this is necessary, some are the defaults
# but it never hurts to be cautious
my $startup_code= <<STARTUP_CODE;
crash_dumps_octave_core(0);
page_screen_output(0);
silent_functions(1);
page_screen_output(0);
page_output_immediately(1);
warning( "off", "Octave:fortran-indexing" );
warning( "off", "Octave:empty-list-elements" );
more off;
suppress_verbose_help_message(1);
STARTUP_CODE
$o->interpret( $startup_code ); # check return value?
return;
}
# we get here from a SIG{CHLD} or a SIG{PIPE}.
# if it's the octave process, then we want to deal
# with it, if it isn't, then we want to pass it to
# the calling processes handler. But how can we
# do that reliably?
#
# instead we just reap any dead processes
sub reap_interpreter
{
# print "REAP_INTERPRETER\n";
my $o= $octave_object;
my $pid= $octave_object->{octave_pid};
return unless $pid;
if ( waitpid($pid, WNOHANG) > 0 ) {
$octave_object->{OCTIN} = "";
$octave_object->{OCTOUT} = "";
$octave_object->{octave_pid} = "";
}
while (
( my $reaped = waitpid (-1, WNOHANG) ) > 0
) { };
return;
}
sub stop_interpreter
{
my $o = shift;
my $Oin= $octave_object->{OCTIN};
my $Oout= $octave_object->{OCTOUT};
return unless $Oin and $Oout;
print $Oin "\n\nexit\n";
#<$Oin>; #clean up input - is this required?
close $Oin;
close $Oout;
$octave_object->{OCTIN} = "";
$octave_object->{OCTOUT} = "";
$octave_object->{octave_pid} = "";
return;
}
# send a string to octave and get the result
sub interpret
{
my $o = shift;
my $cmd= shift;
my $marker= $octave_object->{MARKER};
my $Oin= $octave_object->{OCTIN};
my $Oerr= $octave_object->{OCTERR};
my $select= $octave_object->{SELECT};
my $pid = $octave_object->{octave_pid};
croak "octave interpreter not alive" unless $Oin and $Oerr;
# set SIGnals here, and they will be reset to what the
# user set them to outside
local $SIG{CHLD}= \&reap_interpreter;
local $SIG{PIPE}= \&reap_interpreter;
# print STDERR "INTERP: $cmd\n";
# $Oin = "\n\n$cmd\ndisp('$marker');fflush(stdout);\n";
# $pid->pump() while length $Oin;
print $Oin "\n\n$cmd\ndisp('$marker');fflush(stdout);\n";
my $input= '';
my $marker_len= length( $marker )+1;
while ( 1 ) {
for my $fh ( $select->can_read() ) {
if ($fh eq $Oerr) {
process_errors();
} else {
sysread $fh, (my $line), 16386;
$input.= $line;
# delay if we're reading nothing, not sure why select doesn't block
select undef, undef, undef, 0.5 unless $line;
}
}
last if $input && substr( $input, -$marker_len, -1) eq $marker;
# $pid->pump();
process_errors() if $Oerr;
# select undef, undef, undef, 0.5 unless $line;
# last if substr( $Oout, -$marker_len, -1) eq $marker;
}
# we need to leave octave blocked doing something,
# otherwise it can't handle a CTRL-C
print $Oin "\n\nfread(stdin,1);\n";
return substr($input,0,-$marker_len);
}
# process any input of stderr
# we assume that we will get a line with
# error: or warning:
sub process_errors
{
my $Oerr= $octave_object->{OCTERR};
my $select= IO::Select->new( $Oerr );
my $input= "\n";
# to get full error buffer, wait until we have 100ms with
# not stderr input
while ( my @fh = $select->can_read(0.1) ) {
sysread $fh[0], (my $line), 1024;
last unless $line;
$input.= $line;
}
#parse input, looking for warning and error patterns
# print STDERR "#########$input########\n";
my ($error, $warning);
( run in 2.089 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )