Perlを編集しながら評価できるEditor


http://twitter.com/monjudoh/status/2038844182
この発言に触発されて、あると便利だよなぁと作ってみました。

とりあえずなので、上のペインで Ctrl+Enterを押すと書いてあるコードが Perlでevalされて下のペインに表示されます。選択している場合には選択していた場所だけが実行されます。機能はそれだけです。
Perlの評価部分に関しては以下の ipl.batを拝借しています。
日記帳だ! with Tux on Libserver :: irbをパクってiplを完成させてみた

以下の2つのファイルがコードの全体です。
実行するには、Perl5.10とJava6*1が必要になります。
MainFrame.java

import java.awt.event.*;
import java.io.*;
import javax.swing.*;

public class MainFrame extends JFrame {
	private final ProcessBuilder builder_;
	private Process process_;
	
	private final JTextArea inputArea_ = new JTextArea(5, 20);
	private final JTextArea outputArea_ = new JTextArea(3, 20);

	public MainFrame() {
		super("Commander");
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		builder_ = new ProcessBuilder("perl", "ipl.pl");
		builder_.redirectErrorStream(true);
		
		JSplitPane root = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
				new JScrollPane(inputArea_), new JScrollPane(outputArea_));
		
		add(root);
		this.pack();
	}

	public void start() throws IOException {
		process_ = builder_.start();

		final Thread isThread = new Thread(new Runnable() {
			@Override
			public void run() {
				BufferedReader reader = new BufferedReader(new InputStreamReader(process_.getInputStream()));
				while (!Thread.currentThread().isInterrupted()) {
					try {
						final String line = reader.readLine();
						if (line == null)
							break;
						SwingUtilities.invokeLater(new Runnable() {
							public void run() {
								if (outputArea_.isShowing()) {
									outputArea_.append(line);
									outputArea_.append("\r\n");
								}
							};
						});
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}
		}, "InputStreamThread");
		isThread.start();

		final BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(process_.getOutputStream()));
		inputArea_.addKeyListener(new KeyAdapter() {
			@Override
			public void keyPressed(KeyEvent e) {
				if (e.getKeyCode() == KeyEvent.VK_ENTER && e.isControlDown()) {
					e.consume();
					
					String text = inputArea_.getSelectedText();
					if (text == null || text.length() == 0) {
						text = inputArea_.getText();
					}
					try {
						writer.append(text);
						writer.append("\r\n");
						writer.flush();
					} catch (IOException e1) {
						e1.printStackTrace();
					}
				}
			}
		});

		this.addWindowListener(new WindowAdapter() {
			@Override
			public void windowClosing(WindowEvent e) {
				if (process_ != null) {
					try {
						isThread.interrupt();
						writer.append("exit\r\n");

						process_.destroy();
						process_.waitFor();
					} catch (Exception e1) {
						e1.printStackTrace();
					}
					process_ = null;
				}
			}
		});
	}

	public static void main(String[] args) {
		MainFrame f = new MainFrame();
		f.setVisible(true);
		try {
			f.start();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}

ipl.pl

#!/usr/bin/perl
use feature ':5.10';
use List::Util qw(first max maxstr min minstr reduce shuffle sum);
use Data::Dumper;
local $Data::Dumper::Terse  = 1;
local $Data::Dumper::Indent = 0;
use encoding 'shiftjis';

use IO::Handle;
autoflush STDOUT;
sub p {
  @_=($_) if !@_;
  while(1){
    my $d=Dumper(shift(@_));
    $d=~s/\\x{([0-9a-f]{4})}/chr(hex($1))/eg;
    print $d;
    last if !@_;
    print ',';
  }
  print "\n";
  return undef
}
sub fr($) {
  open my $fh,"<",shift(@_);
  my @res=<$fh>;
  close $fh;
  return @res;
}
sub fw($@) {
  open my $fh,">",shift(@_);
  print $fh join("\n", @_ ),"\n";
  close $fh;
}
sub fa($@) {
  open my $fh,">>",shift(@_);
  print $fh join("\n", @_ ),"\n";
  close $fh;
}
sub ls($) {
  opendir my $fh,shift(@_);
  my @res=readdir($fh);
  closedir $fh;
  return grep( $_ !~ m/^\.\.?$/ , @res );
}

my $__spool__ = '';
my $__tarminator__ = undef;
my ($ANS,@ANS);
while (1) {
  if(!defined $__tarminator__){ print '> '; }
  #else                        { print '* '; }
  my $__line__ = <>;
	print $__line__;
  if(defined $__tarminator__){
    if   (!defined $__line__)                           { $__tarminator__=undef; $__spool__ = ''; next; }
    elsif($__tarminator__ eq '' && $__line__=~m/^\s+$/) { $__tarminator__=undef; }
    elsif($__line__ =~ m/^##.*$__tarminator__\s*$/)     { $__tarminator__=undef; }
  }else{
    if   ($__line__ =~ m/^##.*?(\S+)\s*$/) { $__tarminator__=$1; $__line__=''; }
    elsif($__line__ =~ m/{\s*$/)           { $__tarminator__=''; }
  }
  $__spool__ .= $__line__;
  if (!defined $__tarminator__) {
    @ANS = do {
      eval($__spool__);
    };
    if($@){
      print $@;
    }else{
      if(@ANS && ($__spool__ !~ m/;;\s*$/)){
        print "=> ";
        p @ANS;
      }
    }
    $__spool__ = '';
  }
}

*1:確認していないけど、Java5でも動くかも?