それは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と称して作りこんでいたモジュールはどうも標準ライブラリでいうところのScanner(java.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 |
スキャナのデフォルト基数を指定された基数に設定します。 |