JavaのCoreクラスをぼやっと眺めていると、なかなか面白いクラスがあることが判ります。ここで紹介するjava.io.StreamTokenizerクラスは入力ストリームの字句解析をする機能を提供します。この機能に構文解析機能を組み合わせれば、プログラミング言語の処理系を作ることができるわけで、なかなか興味深いクラスです。
Unix系のシステムを使っている人なら、lexというプログラムのことを聞いたことがある人は多いと思いますが、実際に使ったことがある人は結構少ないでしょう。ちなみにGNUプロジェクトではflexという名前でプログラムが提供されています。lexは字句解析を行うプログラムを生成するプログラムですが、StreamTokenizerは自身が字句解析を行います。多少の設定で挙動を変える事もできます。
字句解析というのは、要するに何かのソースファイルを読み出して、トークンと呼ばれる要素に分解する処理です。トークンというのは、例えばwhileやyourNameといった語(word)や、引用符に囲まれた文字列、括弧やプラス等の記号、数値といったものです。StreamTokenizerの場合、コンストラクタで指定されるReaderで読み出す内容を解析して、nextTokenメソッドにより、1つずつトークンを返します。
といってもよく解らないと思いますので、簡単なサンプルを書いてみましょう。指定したファイルを読み出して、トークンに分解するプログラムです。設定としては、
import java.io.StreamTokenizer; import java.io.FileReader; import java.io.FileNotFoundException; import java.io.IOException; public class TokenizerTest { public final static char QUOTE = '\''; public final static char DOUBLE_QUOTE = '"'; public TokenizerTest(String inputFileName) throws FileNotFoundException { FileReader fr = new FileReader(inputFileName); tokenizer = new StreamTokenizer(fr); tokenizer.resetSyntax(); tokenizer.wordChars('0', '9'); tokenizer.wordChars('a', 'z'); tokenizer.wordChars('A', 'Z'); tokenizer.wordChars('_', '_'); tokenizer.whitespaceChars(' ', ' '); tokenizer.whitespaceChars('\t', '\t'); tokenizer.whitespaceChars('\n', '\n'); tokenizer.whitespaceChars('\r', '\r'); tokenizer.quoteChar(QUOTE); tokenizer.quoteChar(DOUBLE_QUOTE); tokenizer.parseNumbers(); tokenizer.eolIsSignificant(false); tokenizer.slashStarComments(true); tokenizer.slashSlashComments(true); } public void printToken() throws IOException { int token; while ((token = tokenizer.nextToken()) != StreamTokenizer.TT_EOF) { switch (token) { case StreamTokenizer.TT_EOL: System.out.println("<EOL/>"); break; case StreamTokenizer.TT_NUMBER: System.out.println("<number>" + tokenizer.nval + "</number>"); break; case StreamTokenizer.TT_WORD: System.out.println("<word>" + tokenizer.sval + "</word>"); break; case QUOTE: System.out.println("<char>" + tokenizer.sval + "</char>"); break; case DOUBLE_QUOTE: System.out.println("<string>" + tokenizer.sval + "</string>"); break; default: System.out.print("<token>" + (char)tokenizer.ttype + "</token>"); } } } private StreamTokenizer tokenizer; public static void main(String[] args) throws Exception { if (args.length != 1) { System.err.println("Usage: TokenizerTest file"); System.exit(1); } TokenizerTest tt = new TokenizerTest(args[0]); tt.printToken(); } }
実行方法はjavacでコンパイルの上、
% java TokenizerTest 適当なソースファイルとしてみてください。StreamTokenizerが、何をトークンだと思うかが判るのではないでしょうか。