次のトークンを事前に読み取り、それを格納し、それに対してテストすることにより、unread
orの必要性を回避する再帰パーサーを実装する一般的な方法があります。peek
- トークンを読み込むときは、それを (グローバル) 変数に格納します。
- 次に、探しているすべてのトークンを使用してそれに対してテストするだけです(例
<li>
および</ul>
)
- 正しいものが見つかったら、それを処理するメソッドを呼び出します (または続行します)。
- (一致するものを「消費」して、次のトークンを読み取ります)
事実上、あなたはすでに先を見ています。
Dragon コンパイラブックの第 1 版では、初期の概要の章に C の良い例があります (第 2 版では Java を使用していますが、不必要に誇張されています。私見 - C スタイルは Java で正常に動作します)。
私自身のソース コードから例を抽出しようとしますが、私のコードは、使いやすいものを処理するためのメソッドを含むライブラリ レイヤーに分離されています。それらを組み合わせて明確な例を作成しようとしますが、おそらくスタンドアロンでは実行されません。アイデアを説明するための疑似コードと考えてください。ギャップを埋める必要があります。
XMLStreamReader in;
int token;
String localname;
public void parse() {
next();
if (token==START_ELEMENT && localname.equals("ul")) ul();
}
void ul() {
next(); // assume we are called when a <ul> is seen, so we consume it
while (true) { // loops for list
if (token==START_ELEMENT && localname.equals("li")) li(); // ifs for choice
else if (token==START_ELEMENT && localname.equals("sometag")) sometag();
else break;
}
if (token==END_ELEMENT && localname.equals("ul")) next();
else throw new RuntimeException("expected </ul>");
// <li> or <sometag> would also be acceptable
}
void li() {
next();
...
}
void next() {
token = in.next(); // consume the token means to set up the next one
localname = in.getLocalName();
}
反復的なものを処理するためにレイヤーライブラリを作成すると、はるかに使いやすいことがわかりました。たとえば、次のようなものがあります。
boolean startTag(String name)
一致する場合にのみ true を返します
void requireStartTag(String name)
一致する場合は消費し、一致しない場合は例外をスローします
しかし、例はすべて文字通りのままにしておくとより明確になると思います。
また、要素以外のトークン (コメント、PI など) をスキップするなどの問題もあります。より役立つ例外などのために、現在の行を追跡します。