M12i.

学術書・マンガ・アニメ・映画の消費活動とプログラミングについて

それはParserというよりScannerだったのか

プログラミングの用語法とJava標準ライブラリのAPIに関するちょっとした話。結論を先にいえば「標準ライブラリ」についてきちんと理解しておくことは生産性の観点で重要」というそれだけの話です。

以前、JP1/AJS2(日立製のスイートに含まれるジョブ実行管理システム)のユニット定義情報をパースしてJavaコードからのアクセスを可能にするライブラリをつくったのですが、そのなかでParserという言葉にちょっとしっくり来ないものを感じていました(JP1/AJS2のユニット定義パースライブラリはこちら。そこから分離したパーサライブラリはこちら)。

Parserという場合、いっぱんに字句解析→構文解析という2段階の処理を行う前提があって、とくに前者の部分はLexerとかTokenizerとか呼ばれるのが一般的なようです。そして後者が狭義のParserというわけです。

実際、正規表現コンパイラとランタイムを実装するときなどにはこの2段階構成が概念整理的にもモジュールの役割分担的にも便利に思えました。が、上述のJP1/AJS2のユニット定義ファイルに関していうとトークンレベルの仕様書は見つからず、構文レベルの仕様書を見たところ各種の識別子やリテラルそれらの出現順序について「ああも書ける」「こうも書ける」という感じでなんだか煩雑。

結果として「だったらもうトークン化しながらその場でユニット定義のツリー構造を組み上げてしまえ」ということになり、Parserのモジュールのなかで識別子、カーリーブラッケット、イコール、空白文字、文字列っぽいもの、タプルもどきな何か、などなどについて順次読み取りをおこなう実装となりました。『JavaScript: The Good Parts』に示されているJSONパーサに影響を受けたのかもしれません。

それが、このかんオラクル認定資格のJavaプログラマ Gold SE 8のために旧PCJ-Pの復習をしていて気がついたのですが、私がParserと称して作りこんでいたモジュールはどうも標準ライブラリでいうところのScannerjava.util.Scanner)ではないかと思い始めた次第(参考までそのメソッド一覧を下段に転載)。

知っていればこのクラスを拡張したり模倣したりすることでいろいろ時間の節約にもなった気がします。標準ライブラリについての理解というのはやはり重要だなぁという話です。

メソッドと引数 戻り値 説明
close()   void 現在のスキャナをクローズします。
delimiter()   Pattern この Scanner が区切り文字のマッチングに現在使用している Pattern を返します。
findInLine (Pattern pattern)   String 区切り文字を無視して、次に現れる指定されたパターンの検索を試みます。
findInLine (String pattern)   String 区切り文字を無視して、次に現れる、指定された文字列から構築されたパターンの検索を試みます。
findWithinHorizon (Pattern pattern, int horizon)   String 次に現れる指定されたパターンの検索を試みます。
findWithinHorizon (String pattern, int horizon)   String 区切り文字を無視して、次に現れる、指定された文字列から構築されたパターンの検索を試みます。
hasNext()   boolean このスキャナが入力内に別のトークンを保持する場合は true を返します。
hasNext (Pattern pattern)   boolean 次の完全なトークンが指定されたパターンに一致する場合は true を返します。
hasNext (String pattern)   boolean 次のトークンが、指定された文字列から構築されたパターンに一致する場合は true を返します。
hasNextBigDecimal()   boolean このスキャナの入力内の次のトークンが、nextBigDecimal() メソッドを使って BigDecimal 値として解釈可能な場合に、true を返します。
hasNextBigInteger()   boolean このスキャナの入力内の次のトークンが、nextBigInteger() メソッドを使ってデフォルトの基数の BigInteger 値として解釈可能な場合に、true を返します。
hasNextBigInteger (int radix)   boolean トークンを整数として解釈するために使用する基数 このスキャナの次のトークンが有効な BigInteger である場合にのみ、trueこのスキャナがクローズしている場合
hasNextBoolean()   boolean 文字列「true・false」から作成された大文字と小文字の区別されないパターンを使用して、スキャナの入力内の次のトークンを boolean 値として解釈可能であれば、true を返します。
hasNextByte()   boolean このスキャナの入力内の次のトークンが、nextByte() メソッドを使ってデフォルトの基数の byte 値として解釈可能な場合に、true を返します。
hasNextByte (int radix)   boolean このスキャナの入力内の次のトークンが、nextByte() メソッドを使って指定された基数の byte 値として解釈可能な場合に、true を返します。
hasNextDouble()   boolean このスキャナの入力内の次のトークンが、nextDouble() メソッドを使って double 値として解釈可能な場合に、true を返します。
hasNextFloat()   boolean このスキャナの入力内の次のトークンが、nextFloat() メソッドを使って float 値として解釈可能な場合に、true を返します。
hasNextInt()   boolean このスキャナの次のトークンが有効な int 値である場合にのみ、true このスキャナの次のトークンが有効な int 値である場合にのみ、trueこのスキャナがクローズしている場合
hasNextInt (int radix)   boolean このスキャナの入力内の次のトークンが、nextInt() メソッドを使って指定された基数の int 値として解釈可能な場合に、true を返します。
hasNextLine()   boolean このスキャナの入力に別の行がある場合は true を返します。
hasNextLong()   boolean このスキャナの次のトークンが有効な long 値である場合にのみ、trueこのスキャナがクローズしている場合
hasNextLong (int radix)   boolean このスキャナの入力内の次のトークンが、nextLong() メソッドを使って指定された基数の long 値として解釈可能な場合に、true を返します。
hasNextShort()   boolean このスキャナの次のトークンがデフォルト基数に基づく有効な short 値である場合にのみ、trueこのスキャナがクローズしている場合
hasNextShort(int radix)   boolean このスキャナの入力内の次のトークンが、nextShort() メソッドを使って指定された基数の short 値として解釈可能な場合に、true を返します。
ioException()   IOException この Scanner の基になる Readable が最後にスローした IOException を返します。
locale()   Locale 現在のスキャナのロケールを返します。
match()   MatchResult このスキャナが実行した最後のスキャン操作のマッチング結果を返します。
next()   String このスキャナから次の完全なトークンを検索して返します。
next(Pattern pattern)   String 指定されたパターンに一致する次のトークンを返します。
next(String pattern)   String 次のトークンが指定された文字列から構築されたパターンに一致する場合にのみ、それを返します。
nextBigDecimal()   BigDecimal 入力の次のトークンを BigDecimal としてスキャンします。
nextBigInteger()   BigInteger 入力の次のトークンを BigInteger としてスキャンします。
nextBigInteger (int radix)   BigInteger 入力の次のトークンを BigInteger としてスキャンします。
nextBoolean()   boolean 入力の次のトークンを boolean 値としてスキャンして、その値を返します。
nextByte()   byte 入力の次のトークンを byte としてスキャンします。
nextByte (int radix)   byte 入力の次のトークンを byte としてスキャンします。
nextDouble()   double 入力の次のトークンを double としてスキャンします。
nextFloat()   float 入力の次のトークンを float としてスキャンします。
nextInt()   int 入力の次のトークンを int としてスキャンします。
nextInt(int radix)   int 入力の次のトークンを int としてスキャンします。
nextLine()   String スキャナを現在行の先に進めて、スキップした入力を返します。
nextLong()   long 入力の次のトークンを long としてスキャンします。
nextLong(int radix)   long 入力の次のトークンを long としてスキャンします。
nextShort()   short 入力の次のトークンを short としてスキャンします。
nextShort (int radix)   short 入力の次のトークンを short としてスキャンします。
radix()   int 現在のスキャナのデフォルト基数を返します。
remove()   void Iterator の実装が remove オペレーションをサポートしない場合
reset()   Scanner 現在のスキャナをリセットします。
skip(Pattern pattern)   Scanner 指定されたパターンに一致する入力を、区切り文字を無視してスキップします。
skip(String pattern)   Scanner 指定された文字列で構築されたパターンに一致する入力をスキップします。
toString()   String  この Scanner の文字列表現を返します。
useDelimiter (Pattern pattern)   Scanner このスキャナの区切り文字パターンを、指定されたパターンに設定します。
useDelimiter (String pattern)   Scanner このスキャナの区切り文字パターンを、指定された String から作成されたパターンに設定します。
useLocale (Locale locale)   Scanner スキャナのロケールを指定されたロケールに設定します。
useRadix (int radix)   Scanner スキャナのデフォルト基数を指定された基数に設定します。