The AWK~plus Programming Language

AWK~plus

AWK の彼方に (Over The AWK)

The AWK~plus Programming Language は、
  • プログラム言語AWK (The AWK Programming Language) 仕様と GNU GAWK の主要な拡張機能の実装
  • 型推論と 関数型プログラミングによるシンプルな外観
  • 言語レベルでの ロックフリーでスレッドセーフな並列プログラミングのサポート
  • 動的型付 (インタプリタ) と 静的型付け (コンパイラ) をハイブリッドでサポートする型システム
などの特徴を持つ 次世代スクリプト実行環境です。

互換性 (Compatibility)

Note:  プログラム言語AWKの例題をそのまま実行可能であり、実行結果は GNU GAWK とバイナリレベルで一致する。 (コンパイラは一部、非互換あり)

Note:  正規表現、printf フォーマット変換などは Java仕様を採用しており `awk4j' の方が GNU GAWK との互換性は高い。

オブジェクト指向 (Object Oriented)

関数型プログラミング (Functional programming)

関数値 (Function value)

関数値は他の関数と同じように呼び出せる関数オブジェクトで、 apply メソドが呼び出されたときに実行される。

関数値 = '.' 関数呼び出し ;

function add(a, b) { return a + b }
 function calc(x: Function) { return x.apply } # 関数値呼び出し
 print calc(.add(1, 2)) # 3

ダックタイピング (Duck typing)

関数値を使用して クラスの継承関係を無視した メソッド呼び出しが 安全に実行できる。

 if (duck is bird.Duck) duck.quacks() # duckがクラス bird.Duckのインスタンスなら quacksを呼び出す
 if (.quacks() in duck) duck.quacks() # インスタンス duckにメソド quacksが存在すれば quacksを呼び出す

Note:  duck typing
In Computer Programming, duck typing is a style of dynamic typing in which an object's current set of methods and properties determines the valid semantics, rather than its inheritance from a particular class. The name of the concept refers to the duck test, attributed to James Whitcomb Riley, which may be phrased as follows: If it walks like a duck and quacks like a duck, I would call it a duck.

リスナーの実装 (Listener)

関数値を指定して、リスナーを生成できる。 (sample/ImageViewer6.awk 参照)

val baseurl = ARGV[1]
val imageLabel = new javax.swing.JLabel("", javax.swing.SwingConstants.CENTER)
val frame = new javax.swing.JFrame("AWK~Plus - ImageViewer")
BEGIN {
  val DELAY_SECONDS = 60
  val FRAME_HEIGHT = 320
  val FRAME_WIDTH = 240
  frame.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE)
  val dim = frame.getToolkit().getScreenSize
  frame.setLocation(new java.awt.Point(dim.width-7 - FRAME_WIDTH, dim.height-55 - FRAME_HEIGHT))
  val container = frame.getContentPane
      container.add(imageLabel)
      val adapter = new plus.awt.Mouse(.mousePressed(null)) # ヘルパ関数 Mouseで関数値をラッピング
      container.addMouseListener(adapter)
  actionPerformed(null)
  frame.pack
  frame.setVisible(true)
  val actionListener: java.awt.event.ActionListener = # リスナーインターフェイスの動的実装
      plus.reflect.Reflection.implement("java.awt.event.ActionListener", .actionPerformed(null))
  new javax.swing.Timer(1000 * DELAY_SECONDS, actionListener).start
}
function mousePressed(e: java.awt.event.MouseEvent) {
  if (1 != e.getButton) frame.setAlwaysOnTop(!frame.isAlwaysOnTop)
}
function actionPerformed(actionEvent: AnyRef) {
  imageLabel.setIcon(new javax.swing.ImageIcon(baseurl strftime("/HHmm") ".jpg"))
}

スレッドの生成 (Concurrent)

関数値を指定して スレッドを生成できる。 (sample/C2.awk 参照)

var counter: Int = 0
var sum: Int = 0
function atomCount()(n: Int) {
  while (10000 >= (n = ++counter)) sum += n # スレッドセーフな共有変数の更新
}
val T[1] = Future(.atomCount()) # スレッドを生成、これはクロージャ(Closure)
val T[2] = Future(.atomCount) # 引数が空の場合は'()'を省略可能
Join(T); assert(50005000 == sum) # スレッドの終了を待ち合わせる

Note:  クロージャ(Closure) は自由変数を保持した関数オブジェクトで、 見えている変数に対して閉じているためにクロージャと呼ばれる。

Note:  言語レベルで並列プログラミングをサポートしているため、 ロックフリーでスレッドセーフな変数の更新が可能。

Note:  Future など拡張された組み込み関数については lib/predef.awk を参照するとよい。

型変換 (Cast)

変数の型変換は以下のとおり。

 "" X # 文字列に変換
 X -0 # 数値タイプに変換
 int(X) # 組み込み関数を使用してプリミティブ数値に変換 (他に double,float,long)
 val x: タイプ = X # X のタイプはコンパイラが認識している必要がある (Objectタイプは不可)
 val y: タイプ = 連想配列.cast(キー) # 連想配列から強制キャストして読み出す

実行 (Run)

コマンドラインオプション (Options)

-F fs
--field-separator fs
組み込み変数 FS に fs を設定する
-v var=val
--assign var=val
スクリプトが実行される前に変数 var に値 val を設定する。 変数の値は BEGIN ルールの中でも使用可能

実行パラメータ (Parameters)

実行パラメータは ARGV 変数に代入される。 ファイル名の変わりに var=val 形式のパラメータを指定した場合は、 ストリーム参照時にグローバル変数 var へ val を設定する。

インタプリタ (Interpreter)

インタープリタは AWK~plus 言語仕様の全てをサポートする 動的型付けモードで動作するため AWK言語仕様との互換性が高い。

 java plus.Plus ソースファイル名 [[コマンドラインオプション] 実行パラメータ ]

コンパイラ (Compiler)

コンパイラは、 型推論による静的型付けモードで動作する。

# Compile
 java plus.Plus -d 出力ディレクトリ ソースファイル名
# Run
 java クラス名 [[コマンドラインオプション] 実行パラメータ ]

Note:  -d は クラス出力ディレクトリ指定

Note:  スクリプトのコードはプラットフォーム依存であるが 改行は win と unix 形式のどちらでもよい。 Android は utf-8 のため、 コード変換が必要なら nkf (漢字コード変換フィルタ) がおすすめ。

制限事項

サンプル (Sample)

サンプルの実行は以下のとおり。

 make sample

ベンチマーク (Bench mark)

エラトステネスのふるい (Eratosthenes) による、 四則演算、文字列結合、連想配列アクセスを多用 した場合の実行結果を以下に示す。 (想定範囲の結果になっている (^^;)

 make era
« Windows-XP, Java7 »
言語 実行時間(秒) 相対値
Java 0.23 1
Kotlin 0.24 1
Xtend 0.34 1
plus (Kotlin Compiler) 0.86 4
Scala 1.00 4
GNU AWK 4.0.1 1.xx 4
plus (Scala Compiler) 1.34 6
Groovy 2.39 10
Jython (Python) 3.66 16
plus (Interpreter) 8.84 38
plus (Interpreter) 27.80 121

« Ubuntu 12.04 »
言語 実行時間(秒) 相対値
Java 0.31 1
Kotlin 0.34 1
Scala 1.08 3
plus (Kotlin Compiler) 1.24 4
plus (Scala Compiler) 1.80 6
GNU AWK 3.1.8 1.xx 3
plus (Interpreter) 8.03 26
plus (Interpreter) 27.80 121

Note: Winと Linuxの振る舞いの差は、 Java VM (client | server) の違いが大。

Note:  GNU AWK の実行時間は 秒単位。

動作環境 (System requirements)

動作環境は以下のとおりで、別途 Java実行環境 (JRE) を用意すればよい。

Note: Win環境で make が必要なら GNU Make for Windows または mingw-and-ndk などからダウンロードすればよい。

Note: Scala ライブラリは 軽量化のため必要なもののみ組み込んでいる。 Scala の全機能が必要な場合は、 Scalaをダウンロードして パスを通せばよい。


The AWK~plus Programming Language
  1. AWK の彼方に (Over The AWK)
    1. 互換性 (Compatibility)
    2. オブジェクト指向 (Object Oriented)
    3. 関数型プログラミング (Functional programming)
    4. 型変換 (Cast)
  2. 実行 (Run)
    1. インタプリタ (Interpreter)
    2. コンパイラ (Compiler)
    3. サンプル (Sample)
    4. ベンチマーク (Bench mark)
  3. 動作環境 (System requirements)