#!/usr/bin/env perl use strict; use File::Temp 'tempfile'; # MASH: Mathematica Scripting Hack (use mathematica like perl/python/ruby/etc) # by Daniel Reeves, 1999; rewritten in perl for mathematica 6 in 2008. # MASH allows you to have a self-contained mathematica script that can # be executed (with arguments) from the command line and used in a pipeline. # It functions as the interpreter for mathematica scripts by serving as # a proxy between the script and the kernel. Namely, it does the following: # * takes a mathematica source file as its first argument (or from stdin if no # arguments). # * makes the command line arguments available to the mathematica code as a # list called ARGV and defines convenient functions for printing to stdout # and stderr and reading from stdin. This is all done by evaluating the # code in $prescript below. # * evaluates the mathematica script (printing to stdout and stderr only what # it is explicitly told to). # * evaluates the code in $postfix below, including exiting mathematica. # To make a mathematica script foo.m executable: # * make the first line of foo.m be: #!/usr/bin/env /path/to/mash.pl # (or just "#!/usr/bin/env mash" if mash is in $PATH). # * chmod u+x foo.m # When mash receives an interrupt signal (SIGINT) it kills the mathematica # process. (May want to send along SIGINT instead; and do the same for # other signals.) # Possible paths to the mathematica kernel. Add yours. my @mathpath = ( "/usr/bin/math", "/usr/local/bin/math", "/Applications/Mathematica.app/Contents/MacOS/MathKernel", ); my $math; # The first of the above that actually exists. for (@mathpath) { if(-e $_) { $math = $_; last; } } # Evaluate this in mathematica before eval'ing the script. my $prescript = <<'EOF'; # This stuff probably belongs in a package. ARGV = args = Drop[$CommandLine, 4]; (* Command line args. *) pr = WriteString["stdout", ##]&; (* More *) prn = pr[##, "\n"]&; (* convenient *) perr = WriteString["stderr", ##]&; (* print *) perrn = perr[##, "\n"]&; (* statements. *) EOF = EndOfFile; (* I wish mathematica *) eval = ToExpression; (* weren't so damn *) re = RegularExpression; (* verbose! *) read[] := InputString[""]; (* Grab a line from stdin. *) doList[f_, test_] := (* Accumulate list of what f[] *) Most@NestWhileList[f[]&, f[], test]; (* returns while test is true. *) readList[] := doList[read, #=!=EOF&]; (* Slurp list'o'lines from stdin *) cat = StringJoin@@(ToString/@{##})&; (* Like sprintf/strout in C/C++. *) system = Import[cat["!", ##], "Text"]&; (* System call. *) keys = DownValues[#][[All,1,1,1]]&; (* Keys of a hash/dictionary. *) SetAttributes[each, HoldAll]; (* each[pattern, list, body] *) each[pat_, lst_, bod_] := (* converts pattern to body for *) Scan[Replace[#, pat:>bod]&, Evaluate@lst] (* each element of list. *) some[f_, l_List] := (* whether f applied to some *) Scan[If[f[#], Return[True]]&, l] === True (* element of list is True. *) every[f_, l_List] := (* similarly, whether f is *) Scan[If[!f[#], Return[False]]&, l]===Null (* is True for every element. *) pout = pr; (* [Backward compatibility.] *) strout = cat; (* [Backward compatibility.] *) EOF # Evaluate this in mathematica after eval'ing the script. my $postscript = <<'EOF'; Exit[0]; EOF # Slurp up the script, either from file or from stdin. my $script = $ARGV[0]; my @lines; if(defined($script)) { open(F, "<$script") or die qq{Can't open mathematica script "$script": $!\n}; @lines = ; close(F); } else { @lines = ; } # Feed slurped script plus prescript and postscript to temp file. shift(@lines) if $lines[0] =~ /^\#\!/; # exclude the shebang line. my($tmpfh, $tmpf) = tempfile("mash-XXXX", SUFFIX=>'.m', UNLINK=>1); print $tmpfh $prescript, "\n\n", @lines, "\n\n", $postscript; # Open a pipe to mathematica and run the script. my $cmd = "$math -noprompt -run \"<<$tmpf\" " . join(' ', @ARGV); my $pid = open(F, "$cmd |") or die "Can't open pipe from $cmd: $!"; $SIG{'INT'} = sub { kill('KILL', $pid); close(F); die "MASH killed.\n"; }; print while(); close(F); #system("cp $tmpf mash-DEBUG.m"); # keep copy of the script with pre/postscript.