Groovy AST の勉強 その3

AST変換のネタを考えてみた。こんな題材:

クラス定義をせずに記述したスクリプトにおいて print/println 文の出力内容をテキストエリアなどの GUI 部品に表示する。ただし、もとのスクリプトには、GUI 部品の記述は一切なしで。

これ、意外と使えると思うんだけどな。。。

以下、やろうとしていることをサンプルコードで示してみる。

(1) AST変換前のスクリプト

// fib.groovy
int fib (int x) {
  (x<2)?x:(fib(x-2)+fib(x-1))
}
println ((0..10).collect{fib(it)}.join(" "))

フィボナッチ数列を10番目まで表示するスクリプト

これをCONVERSIONフェーズまでコンパイルすると、つぎのような AST に対応するコードになるはず。

(2) CONVERSION後の AST に対応するコード

// fib.groovy (疑似コード)
class fib extends groovy.lang.Script { 

  public fib() {
  }

  public fib(groovy.lang.Binding context) {
    super(context)
  }

  public static void main(java.lang.String[] args) {
    org.codehaus.groovy.runtime.InvokerHelper.runScript(fib, args)
  }

  public int fib (int x) {
    (x<2)?x:(fib(x-2)+fib(x-1))
  }

  public Object run () {
      println ((0..10).collect{fib(it)}.join(" "))
  }
}

で、次のように書き換えてやればいいはず。

(3)AST変換後のスクリプト

// fib.groovy (AST変換後の疑似コード)
class fib extends groovy.lang.Script { 

  public fib() {
  }

  public fib(groovy.lang.Binding context) {
    super(context)
  }

  public static void main(java.lang.String[] args) {
    org.codehaus.groovy.runtime.InvokerHelper.runScript(fib, args)
  }

  // テキストエリアオブジェクトの変数
  private static javax.swing.JTextArea ta=null

  // テキストエリアに出力するprintメソッド
  public static void print (java.lang.Object x) {
    if (ta == null) {
      openWindow("console")
    }
    ta.setText(ta.getText() + x.toString())
    ta.setCaretPosition(ta.getText().length())
  }

  // テキストエリアに出力するprintlnメソッド
  public static void println (java.lang.Object x) {
    print (x.toString() + "\n")
  }

  // テキストエリアのウィンドウフレームを開くメソッド
  private static void openWindow (String title) {
    javax.swing.JFrame f = new javax.swing.JFrame(title)
    f.setSize(800, 600)
    f.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE)
    ta = new javax.swing.JTextArea(100,50)
    ta.setForeground(java.awt.Color.white)
    ta.setBackground(java.awt.Color.black)
    ta.setFont(
      new java.awt.Font("メイリオ", java.awt.Font.PLAIN, 50))
    f.getContentPane().add(
      new javax.swing.JScrollPane(ta), java.awt.BorderLayout.CENTER)
    f.setVisible(true)
  }

  public int fib (int x) {
    (x<2)?x:(fib(x-2)+fib(x-1))
  }

  public Object run () {
    try {

      println ((0..10).collect{fib(it)}.join(" "))

    } catch (Exception e) {
      println e
      for (java.lang.StackTraceElement ste in e.getStackTrace()) {
        println "\tat ${ste}"
      }
    }
  }
}

print/println はオーバーライドしておいて、テキストエリアオブジェクトが null なら openWindow してテキストエリアを持つウィンドウフレームをオープンし、テキストエリアオブジェクトに出力する。

で、print/println/openWindow それからフィールド変数 ta を static にしておくのがミソ。main メソッドのようなクラスメソッドから print/println を呼ぶような実装にも対応できるようにしておく。
それから、例外もキャッチしてテキストエリアに表示する。

まとめると、次のようなことをする ASTTransformation を実装することになる:

(A) メインクラスにフィールド変数 ta を追加。
(B) メインクラスにメソッド print/println/openWindow を追加。
(C) run メソッド内部に try-catch 構造を追加。

次回は実装例を示す。