第025回_条件付字句解析器1_CharDataLexer_CharDataTokenizer
»
基礎クラスの準備ができたので、今回から字句解析器を検討します。
今回はCharDataについて検討します。
# 8個の字句解析器について1回ずつ実装を紹介します。
# 内容が薄く見えますが、前回までの基礎クラスをコツコツと積み上げた
# 結果なので22回、23回、24回を振り返りながら読んでいただけたらと思います。
CharDataを字句解析するCharDataLexerの検討
CharDataの構文を確認した上で検討に入ります。
■CharDataの構文
空のCharDataTokenになります。そうでない場合、この1文字目の"]"はCharDataTokenの内容に含みます。
よって、仕様は「この文字"]"を確定せず、次の文字に移動して、CharDataTokenizer2へ移動する」となります。
■CharDataの構文
[14] CharData ::= [ˆ<&]* − ([ˆ<&]* ']]>' [ˆ<&]*)
とあるので
CHARDATA_WORD は <と&を除く文字列 かつ "]]>" を含まない文字列
また、[ˆ<&]*とあるので
文字数は0文字の場合もあり得ます。
■1文字目による分岐とあるので
CHARDATA_WORD は <と&を除く文字列 かつ "]]>" を含まない文字列
また、[ˆ<&]*とあるので
文字数は0文字の場合もあり得ます。
前にも述べましたが、この自作XMLパーサの役割分担は
1文字目による分岐をLexer
2文字目以降の処理をTokenizer
が担当します。
よってCharDataLexerの考えるべきはバッファから取得した最初の1文字です。
最初の1文字によって
トークンをすぐに作成する
か
必要なTokenizerへ移動する
に分岐します。
処理をまとめると次のように表にできます。
少し小難しいのは、1文字目が"]"の場合です。2文字目が"]"かつ3文字目が">"の場合はこの1文字目の"]"はCharDataTokenには含まず、1文字目による分岐をLexer
2文字目以降の処理をTokenizer
が担当します。
よってCharDataLexerの考えるべきはバッファから取得した最初の1文字です。
最初の1文字によって
トークンをすぐに作成する
か
必要なTokenizerへ移動する
に分岐します。
処理をまとめると次のように表にできます。
次の文字 | 処理 |
――――――――――――――――――――――――――――――――――――――― | |
& | 空のCharDataTokenを作成する |
< | 空のCharDataTokenを作成する |
] | この文字"]"を確定せず、次の文字に移動して、CharDataTokenizer2へ移動する |
その他 | 初期化して、この文字を確定する。さらにCharDataTokenizerへ移動する |
EOF | 空のCharDataTokenを作成する |
空のCharDataTokenになります。そうでない場合、この1文字目の"]"はCharDataTokenの内容に含みます。
よって、仕様は「この文字"]"を確定せず、次の文字に移動して、CharDataTokenizer2へ移動する」となります。
CharDataLexerの実装
CharDataLexerの実装は、MapSwitchLexerを利用して次のようにできます。
public class CharDataLexer extends MapSwitchLexer
{
//空用のクラス
private class CharDataTokenMaker implements Functor
{
public CharDataTokenMaker()
{
}
@Override
public Token tokenize(StringBuilder str, int pos)
{
m_tokenBuilder.initStart();
return new CharDataToken();
}
}
private static final CharDataLexer m_instance = new CharDataLexer();
private CharDataLexer()
{
super();
final CharDataTokenMaker cdtm = new CharDataTokenMaker();
//次の文字 処理
//-----------------------------------------------------------------------------------------
//"&" 空のCharDataTokenを作成する
//"<" 空のCharDataTokenを作成する
//"]" 今の文字"]"を確定せず、次の文字に移動して、
// CharDataTokenizer2へ移動する
//その他 初期化して、今の文字を確定する。
// さらにCharDataTokenizerへ移動する
//EOF 空のCharDataTokenを作成する
MovePos charDataTokenizer2
= new MovePos(new CharDataTokenizer2());
Init_MovePos charDataTokenizer
= new Init_MovePos(new CharDataTokenizer());
m_map.put('&', cdtm);
m_map.put('<', cdtm);
m_map.put(']', charDataTokenizer2);
m_map.setOutOfRange(charDataTokenizer);
m_map.setEofFunctor(cdtm);
}
public static CharDataLexer getInstance()
{
return m_instance;
}
}
CharDataTokenizerの検討
CharDataLexerで1文字目が"その他"の場合に2文字目以降に対して実施するトークナイザがCharDataTokenizerです。
■表Aの処理
字句解析器とトークナイザの実装クラスは--if文を登場せずに--
仕様にある表をソースコードに変換するだけで実装可能になったと思います。
■表Aの処理
CharDataの範囲で1文字取得した場合、どのように処理するかを検討します。
まず、CHARDATA_WORD は "<"と"&"と"]]>"を含まない文字列ですから、
バッファに文字がない場合
1文字目に"<"と"&"と"]"を取得した場合
1文字目が"<"と"&"と"]"でない文字を取得した(その他の)場合
に分岐します。
それぞれの処理をまとめた表Aは
表A
となります。
■表Bの処理
まず、CHARDATA_WORD は "<"と"&"と"]]>"を含まない文字列ですから、
バッファに文字がない場合
1文字目に"<"と"&"と"]"を取得した場合
1文字目が"<"と"&"と"]"でない文字を取得した(その他の)場合
に分岐します。
それぞれの処理をまとめた表Aは
表A
次の文字 | 処理 |
――――――――――――――――――――――――――――――――――――――― | |
& | この文字を確定せずにCharDataTokenを作成する |
< | この文字を確定せずにCharDataTokenを作成する |
] | 確定せずに次の文字のために表Bへ移動する |
その他 | 今の文字を確定する(次の文字も表Aで処理する) |
EOF | 確定せずにCharDataTokenを作成する |
■表Bの処理
表Aにあった表Bについても同じように考えると、
表B (1つ前が]で未確定)
となります。
■表Cの処理表B (1つ前が]で未確定)
次の文字 | 処理 |
――――――――――――――――――――――――――――――――――――――― | |
& | 1文字前を確定して、この文字を確定せずCharDataTokenを作成する |
< | 1文字前を確定して、この文字を確定せずCharDataTokenを作成する |
] | 確定せずに次の文字のために表Cへ移動する |
その他 | 1文字前、今の文字を確定して表Aへ移動する |
EOF | 1文字前を確定して、CharDataTokenを作成する |
表Bにあった表Cについても同じように考えると、
表C (2つ前が]で未確定、1つ前が]で未確定)
となります。
表C (2つ前が]で未確定、1つ前が]で未確定)
次の文字 | 処理 |
――――――――――――――――――――――――――――――――――――――― | |
& | 2文字前、1文字前を確定して、CharDataTokenを作成する |
< | 2文字前、1文字前を確定して、CharDataTokenを作成する |
> | 確定せずにCharDataTokenを作成する |
] | 2文字前を確定する(次の文字も表Cで処理する) | その他 | 2文字前、1文字前、今の文字を確定して表Aへ移動する |
EOF | 2文字前、1文字前を確定して、CharDataTokenを作成する |
CharDataTokenizerの実装
先ほどの検討とMultipleMapsSwitchTokenizerを使ってCharDataTokenizerを実装します。
public class CharDataTokenizer extends MultipleMapsSwitchTokenizer
{
private class CharDataTokenMaker implements Functor
{
public CharDataTokenMaker()
{
}
@Override
public Token tokenize(StringBuilder str, int pos)
{
return new CharDataToken();
}
}
protected final CharDataTokenMaker m_ctm;
public CharDataTokenizer()
{
m_ctm = new CharDataTokenMaker();
makeMap_A();
makeMap_B();
makeMap_C();
}
private void makeMap_A()
{
LexicalMap map = new LexicalMap();
//表A
//次の文字 処理
//-----------------------------------------------------------------------------------------
//"&" この文字を確定せずにCharDataTokenを作成する
//"<" この文字を確定せずにCharDataTokenを作成する
//"]" 確定せずに次の文字のために表Bへ移動する
//その他 今の文字を確定する(次の文字も表Aで処理する)
//EOF 確定せずにCharDataTokenを作成する
// proc1 = m_ctm
MovePos_MoveMap proc2 = new MovePos_MoveMap(1);
Update_MovePos proc3 = new Update_MovePos(this);
map.put('<', m_ctm);
map.put('&', m_ctm);
map.put(']', proc2);
map.setOutOfRange(proc3);
map.setEofFunctor(m_ctm);
m_mapList.add(map);
}
private void makeMap_B()
{
LexicalMap map = new LexicalMap();
//表B (1つ前が]で未確定)
//次の文字 処理
//-----------------------------------------------------
//'&' 1文字前の"]"を確定して、
// この文字を確定せずCharDataTokenを作成する
//'<' 1文字前の"]"を確定して、
// この文字を確定せずCharDataTokenを作成する
//"]" 確定せずに次の文字のために表Cへ移動する
//その他 1文字前、今の文字を確定して表Aへ移動する
//EOF 1文字前を確定して、CharDataTokenを作成する
//
UpdateRange_MovePos proc1
= new UpdateRange_MovePos(m_ctm,1,1);
MovePos_MoveMap proc2
= new MovePos_MoveMap(2);
UpdateRange_MovePos_MoveMap proc3
= new UpdateRange_MovePos_MoveMap(0,1,0);
map.put('&', proc1);
map.put('<', proc1);
map.put(']', proc2);
map.setOutOfRange(proc3);
map.setEofFunctor(proc1);
m_mapList.add(map);
}
private void makeMap_C()
{
LexicalMap map = new LexicalMap();
//表C (2つ前が]で未確定、1つ前が]で未確定)
//次の文字 処理
//-----------------------------------------------------
//'&' 2文字前、1文字前を確定して、CharDataTokenを作成する
//'<' 2文字前、1文字前を確定して、CharDataTokenを作成する
//'>' 確定せずにCharDataTokenを作成する
//"]" 2文字前を確定する(次の文字も表Cで処理する)
//その他 2文字前、1文字前、今の文字を確定して表Aへ移動する
//EOF 2文字前、1文字前を確定して、CharDataTokenを作成する
UpdateRange_MovePos proc1 = new UpdateRange_MovePos(m_ctm,2,1);
// proc2 = m_ctm
UpdateRange_MovePos proc3 = new UpdateRange_MovePos(this,2,2);
UpdateRange_MovePos_MoveMap
proc4 = new UpdateRange_MovePos_MoveMap(0,2,0);
map.put('&', proc1);
map.put('<', proc1);
map.put('>', m_ctm);
map.put(']', proc3);
map.setOutOfRange(proc4);
map.setEofFunctor(proc1);
m_mapList.add(map);
}
}
CharDataTokenizer2の検討
CharDataLexerで1文字目が"]"の場合に、未初期化で2文字目以降に対して実施するトークナイザがCharDataTokenizer2です。
■表Dの処理
■表Dの処理
表D(未初期化、1つ前が]で未確定)
■表Eの処理次の文字 | 処理 |
――――――――――――――――――――――――――――――――――――――― | |
& | 初期化、1文字前を確定してCharDataTokenを作成する |
< | 初期化、1文字前を確定してCharDataTokenを作成する |
] | 確定せずに表Eへ移動する | その他 | 初期化、1文字前、今の文字を確定して表Aへ移動する |
EOF | 初期化、1文字前を確定してCharDataTokenを作成する |
表E(未初期化、2つ前が]、1つ前が]で未確定)
■継承関係次の文字 | 処理 |
――――――――――――――――――――――――――――――――――――――― | |
& | 初期化、2文字前、1文字前を確定して、CharDataTokenを作成する |
< | 初期化、2文字前、1文字前を確定して、CharDataTokenを作成する |
> | 確定せずにCharDataTokenを作成する |
] | 2文字前を確定して表Cへ移動する | その他 | 2文字前、1文字前、今の文字を確定して表Aへ移動する |
EOF | 2文字前、1文字前を確定して、CharDataTokenを作成する |
CharDataTokenizer2は表A(表Aの内部では表Bにも移動する)、表Cに移動する可能性があるためCharDataTokenizerを継承します。
また初期の表はDです。
また初期の表はDです。
CharDataTokenizer2実装
実装は次のようになります。
public class CharDataTokenizer2 extends CharDataTokenizer
{
private class EmptyCharDataTokenMaker implements Functor
{
public EmptyCharDataTokenMaker()
{
}
@Override
public Token tokenize(StringBuilder str, int pos)
{
m_tokenBuilder.initStart();
return new CharDataToken();
}
}
private final EmptyCharDataTokenMaker m_ectm;
public CharDataTokenizer2()
{
super();
//Dからスタートする
m_startindex = 3;
m_currentIndex = m_startindex;
m_ectm = new EmptyCharDataTokenMaker();
makeMap_D();
makeMap_E();
}
private void makeMap_D()
{
LexicalMap map = new LexicalMap();
//表D(未初期化、1つ前が]で未確定)
//次の文字 処理
//-----------------------------------------------------------------------------------------
//"&" 初期化、1文字前を確定してCharDataTokenを作成する
//"<" 初期化、1文字前を確定してCharDataTokenを作成する
//"]" 確定せずに表Eへ移動する
//その他 初期化、1文字前、今の文字を確定して表Aへ移動する
//EOF 初期化、1文字前を確定してCharDataTokenを作成する
//
InitUpdateRange_MovePos proc1
= new InitUpdateRange_MovePos(m_ctm,1,1);
MovePos_MoveMap proc2
= new MovePos_MoveMap(4);
InitUpdateRange_MovePos_MoveMap
proc3 = new InitUpdateRange_MovePos_MoveMap(0,1,0);
map.put('<', proc1);
map.put('&', proc1);
map.put(']', proc2);
map.setOutOfRange(proc3);
map.setEofFunctor(proc1);
m_mapList.add(map);
}
private void makeMap_E()
{
LexicalMap map = new LexicalMap();
//表E(未初期化、2つ前が]、1つ前が]で未確定)
//次の文字 処理
//-----------------------------------------------------------------------------------------
//'&' 初期化、2文字前、1文字前を確定して
// CharDataTokenを作成する
//'<' 初期化、2文字前、1文字前を確定して
// CharDataTokenを作成する
//'>' 確定せずにCharDataTokenを作成する
//"]" 2文字前を確定して表Cへ移動する
//その他 2文字前、1文字前、今の文字を確定して
// 表Aへ移動する
//EOF 2文字前、1文字前を確定して
// CharDataTokenを作成する
//
InitUpdateRange_MovePos proc1
= new InitUpdateRange_MovePos(m_ctm,2,1);
// proc2 = m_ectm
InitUpdateRange_MovePos_MoveMap
proc3 = new InitUpdateRange_MovePos_MoveMap(2,2,2);
InitUpdateRange_MovePos_MoveMap
proc4 = new InitUpdateRange_MovePos_MoveMap(0,2,0);
map.put('<', proc1);
map.put('&', proc1);
map.put('>', m_ectm);
map.put(']', proc3);
map.setOutOfRange(proc4);
map.setEofFunctor(proc1);
m_mapList.add(map);
}
}
仕様にある表をソースコードに変換するだけで実装可能になったと思います。
コメント
コメントを投稿する
SpecialPR