第031回_条件付字句解析器7_WordTokenizer他
»
前回から引き続いてNormalSectionLexerで登場した
WordTokenizer
SymbolTokenizer
SymbolTokenizer2
LeftAngelTokenizer
について説明します。
WordTokenizer
SymbolTokenizer
SymbolTokenizer2
LeftAngelTokenizer
について説明します。
WordTokenizerの実装
public class WordTokenizer extends MapSwitchTokenizer
{
private class WordTokenMaker implements Functor
{
public WordTokenMaker()
{
}
@Override
public Token tokenize(StringBuilder str, int pos)
{
return new WordToken();
}
}
public WordTokenizer()
{
//状態 1つ前の文字は確定済み
// 記号 処理
// -----------------------------------------------------
// #x002D-#x002E 今の文字を確定して次へ
// #x0030-#x003A 今の文字を確定して次へ
// #x0041-#x005A 今の文字を確定して次へ
// #x005F 今の文字を確定して次へ
// #x0061-#x007A 今の文字を確定して次へ
// #x00B7 今の文字を確定して次へ
// #x00C0-#x00D6 今の文字を確定して次へ
// #x00D8-#x00F6 今の文字を確定して次へ
// #x00F8-#x02FF 今の文字を確定して次へ
// #x0300-#x036F 今の文字を確定して次へ
// #x0370-#x037D 今の文字を確定して次へ
// #x037F-#x01FF 今の文字を確定して次へ
// #x200C-#x200D 今の文字を確定して次へ
// #x203F-#x2040 今の文字を確定して次へ
// #x2070-#x218F 今の文字を確定して次へ
// #x2C00-#x2FEF 今の文字を確定して次へ
// #x3001-#xD7FF 今の文字を確定して次へ
// #xF900-#xFDCF 今の文字を確定して次へ
// #xFDF0-#xFFFD 今の文字を確定して次へ
// その他 WordTokenを作成する
// EOF WordTokenを作成する
//
// 処理1:今の文字を確定して次へ
// 処理2:WordTokenを作成する
//
Update_MovePos proc1 = new Update_MovePos();
WordTokenMaker proc2 = new WordTokenMaker();
m_map.put('\u002D', '\u002E', proc1);
m_map.put('\u0030', '\u003A', proc1);
m_map.put('\u0041', '\u005A', proc1);
m_map.put('\u005F' , proc1);
m_map.put('\u0061', '\u007A', proc1);
m_map.put('\u00B7' , proc1);
m_map.put('\u00C0', '\u00D6', proc1);
m_map.put('\u00D8', '\u00F6', proc1);
m_map.put('\u00F8', '\u02FF', proc1);
m_map.put('\u0300', '\u036F', proc1);
m_map.put('\u0370', '\u037D', proc1);
m_map.put('\u037F', '\u1FFF', proc1);
m_map.put('\u200C', '\u200D', proc1);
m_map.put('\u203F', '\u2040', proc1);
m_map.put('\u2070', '\u218F', proc1);
m_map.put('\u2C00', '\u2FEF', proc1);
m_map.put('\u3001', '\uD7FF', proc1);
m_map.put('\uF900', '\uFDCF', proc1);
m_map.put('\uFDF0', '\uFFFD', proc1);
m_map.setOutOfRange(proc2);
m_map.setEofFunctor(proc2);
}
}
SymbolTokenizerの検討
NormalSectionLexerで
SYMBOL ("/>")
を解析するためにSymbolTokenizerを用意します。また、
SYMBOL ("?")、SYMBOL ("?>")
SYMBOL ("]")、SYMBOL ("]]>")
を解析するためにSymbolTokenizer2を用意します。
これまでに、SymbolTokenを生成するクラスとして
JustCreateSymbolTokenizer
SymbolListTokenizer
SymbolListTokenizer2
を用意してきましたのでそれぞれの機能について整理すると
となります。
SymbolTokenizerとSymbolTokenizer2の実装は、一致しない場合に
SymbolTokenを作成する
か
UnknownTokenを作成する
かの違いだけですから、共通の親クラスとして、AbstractSymbolTokenizerを用意します。
SYMBOL ("/>")
を解析するためにSymbolTokenizerを用意します。また、
SYMBOL ("?")、SYMBOL ("?>")
SYMBOL ("]")、SYMBOL ("]]>")
を解析するためにSymbolTokenizer2を用意します。
これまでに、SymbolTokenを生成するクラスとして
JustCreateSymbolTokenizer
SymbolListTokenizer
SymbolListTokenizer2
を用意してきましたのでそれぞれの機能について整理すると
クラス名 | 機能説明 |
JustCreateSymbolTokenizer | 既に読み込んだ文字列をSymbolTokenにする。 |
SymbolTokenizer | 既に読み込んだ文字列に続いて、引数で指定した文字列が一致する場合、SymbolTokenにする。 引数で指定した文字列が一致しない場合、UnknownTokenにする。 |
SymbolTokenizer2 | 既に読み込んだ文字列に続いて、引数で指定した文字列が一致する場合、SymbolTokenにする。 引数で指定した文字列が一致しない場合、既に読み込んだ部分だけをSymbolTokenにする。 |
SymbolListTokenizer | 既に読み込んだ文字列に続いて、引数Listで指定した文字列のいずれかに一致する場合、SymbolTokenにする。 引数Listで指定した文字列のどれにも一致しない場合、UnknownTokenにする。 |
SymbolListTokenizer2 | 既に読み込んだ文字列に続いて、引数Listで指定した文字列のいずれかに一致する場合、SymbolTokenにする。 引数Listで指定した文字列のどれにも一致しない場合、既に読み込んだ部分だけをSymbolTokenにする。 |
SymbolTokenizerとSymbolTokenizer2の実装は、一致しない場合に
SymbolTokenを作成する
か
UnknownTokenを作成する
かの違いだけですから、共通の親クラスとして、AbstractSymbolTokenizerを用意します。
AbstractSymbolTokenizerの実装
public abstract class AbstractSymbolTokenizer extends TermTokenizer
{
private final String m_target;
public AbstractSymbolTokenizer(String target)
{
m_target = target;
}
@Override
public Token tokenize(StringBuilder str, int pos)
{
Token retval = null;
//比較用の文字数を取得する
int endIndex = m_target.length();
int len = str.length();
if (pos+endIndex < len)
{
//対象分の文字を取得する
//memo
// 取得するのはendIndex-1まで
String sub = str.substring(pos, pos+endIndex);
//対象文字と一致する場合
if (sub.equals(m_target))
{
m_tokenBuilder.update(sub);
retval = new SymbolToken();
}
//対象文字と一致しない場合
else
{
retval = makeTokenIfUnmatch();
}
}
else
{
retval = makeTokenIfUnmatch();
}
return retval;
}
protected abstract Token makeTokenIfUnmatch();
}
SymbolTokenizerの実装
public class SymbolTokenizer extends AbstractSymbolTokenizer
{
public SymbolTokenizer(String target)
{
super(target);
}
//マッチしない場合、UnknownTokenを作成する
protected Token makeTokenIfUnmatch()
{
return new UnknownToken();
}
}
SymbolTokenizer2の実装
public class SymbolTokenizer2 extends AbstractSymbolTokenizer
{
public SymbolTokenizer2(String target)
{
super(target);
}
//マッチしない場合もSymbolを作成する
protected Token makeTokenIfUnmatch()
{
return new SymbolToken();
}
}
LeftAngelTokenizerの検討と実装
■検討
LeftAngleTokenizerは、SymbolTokenizer2で上手く解析できないので別クラスとして用意します。
"<"から始まるSYMBOLの整理
■実装"<"から始まるSYMBOLの整理
'<'
'</'
'<?'
'<?xml'
'<!['
'<![CDATA['
'<!ATTLIST'
'<!DOCTYPE'
'<!ENTITY'
'<!ELEMENT'
'<!NOTATION'
COMMENT (<!--)
の12個があります。
SymbolTokenizer2で実装できない理由'</'
'<?'
'<?xml'
'<!['
'<![CDATA['
'<!ATTLIST'
'<!DOCTYPE'
'<!ENTITY'
'<!ELEMENT'
'<!NOTATION'
COMMENT (<!--)
の12個があります。
<?xmlと<?の仕様について考える必要があります。
<?はPI命令の最初のトークン
<?xmlはXML文書宣言の最初のトークン
で、PI命令の対象名としてxmlを指定することはできません。
ただし、PI命令の対象名の構文は、
PITarget ::= Name - ( ( 'X' | 'x' ) ( 'M' | 'm' ) ( 'L' | 'l' ) )
ですから、xmlstylesheetなどのxmlから始まる文字列があってもかまいません。
つまり、<?xmlの時点では確定できず、
となり、SymbolTokenizer2では実装できないことがわかります。
<?はPI命令の最初のトークン
<?xmlはXML文書宣言の最初のトークン
で、PI命令の対象名としてxmlを指定することはできません。
ただし、PI命令の対象名の構文は、
PITarget ::= Name - ( ( 'X' | 'x' ) ( 'M' | 'm' ) ( 'L' | 'l' ) )
ですから、xmlstylesheetなどのxmlから始まる文字列があってもかまいません。
つまり、<?xmlの時点では確定できず、
次の文字がWHITE_SPACEの場合 | : | SYMBOL ('<?xml') |
次の文字がWHITE_SPACEでない場合 | : | SYMBOL ('<?') その後ろはWORDとして切り出す |
public class LeftAngleTokenizer extends MultipleMapsSwitchTokenizer
{
private class DtdSymbolTokenMaker implements Functor
{
private final SymbolListTokenizer2 m_dtdSymbolTokenizer;
public DtdSymbolTokenMaker()
{
ArrayList<String> list = new ArrayList<String>();
list.add("!ATTLIST");
list.add("!DOCTYPE");
list.add("!ENTITY");
list.add("!ELEMENT");
list.add("!NOTATION");
m_dtdSymbolTokenizer = new SymbolListTokenizer2(list);
}
@Override
public Token tokenize(StringBuilder str, int pos)
{
//1つ行き過ぎてるのでpos-1する
return m_dtdSymbolTokenizer.tokenize(str, pos-1);
}
}
private final JustCreateSymbolTokenizer m_symbolMaker;
private final SymbolTokenizer2 m_leftBracket_Tokenizer;
public LeftAngleTokenizer()
{
m_symbolMaker = new JustCreateSymbolTokenizer();
m_leftBracket_Tokenizer = new SymbolTokenizer2("CDATA[");
makeMap_A(); //0
//map B //1
Character[] clist1 = {'x','X'};
makeMap_CharList_MovePosMoveMap(clist1,2);
//map C //2
Character[] clist2 = {'m','M'};
makeMap_CharList_MovePosMoveMap(clist2,3);
//map D //3
Character[] clist3 = {'l','L'};
makeMap_CharList_MovePosMoveMap(clist3,4);
makeMap_E(); //4
makeMap_F(); //5
makeMap_G(); //6
}
//表B,C,Dはほぼ同じなので作成用のメソッドを用意する
private void makeMap_CharList_MovePosMoveMap
(Character[] charList, int toMap)
{
//
//表 B,C,D
// 記号 処理
// -----------------------------------------------------
// charList[0] 今の文字を確定せずにtoMapへ
// charList[1] 今の文字を確定せずにtoMapへ
// ・・・・・
// charList[N] 今の文字を確定せずにtoMapへ
// その他 SYMBOLを作成する
// EOF SYMBOLを作成する
//
// 処理1:今の文字を確定せずにtoMapへ
// 処理2:SYMBOLを作成する
//
MovePos_MoveMap proc1 = new MovePos_MoveMap(toMap);
// proc2 = m_symbolMaker
LexicalMap map = new LexicalMap();
for(Character c : charList)
{
map.put(c , proc1);
}
map.setOutOfRange(m_symbolMaker);
map.setEofFunctor(m_symbolMaker);
m_mapList.add(map);
}
private void makeMap_A()
{
//
//表 A 前の文字が"<"で確定している状態
// 記号 処理
// -----------------------------------------------------
// "/" 今の文字を確定して SYMBOLを作成する
// "?" 今の文字を確定してBへ
// "!" 今の文字を確定せずにFへ
// その他 SYMBOLを作成する
// EOF SYMBOLを作成する
//
// 処理1:今の文字を確定して SYMBOLを作成する
// 処理2:今の文字を確定してBへ
// 処理3:今の文字を確定せずにFへ
// 処理4:SYMBOLを作成する
//
Update_MovePos proc1 = new Update_MovePos(m_symbolMaker);
Update_MovePos_MoveMap proc2 = new Update_MovePos_MoveMap(1);
MovePos_MoveMap proc3 = new MovePos_MoveMap(5);
// proc4 = m_symbolMaker
LexicalMap map = new LexicalMap();
map.put('/', proc1);
map.put('?', proc2);
map.put('!', proc3);
map.setOutOfRange(m_symbolMaker);
map.setEofFunctor(m_symbolMaker);
m_mapList.add(map);
}
private void makeMap_E()
{
//
//表E 確定文字が<? で3つ前がx|X ,
// 2つ前がm|M,1つ前がl|Lで未確定の状態
// 記号 Unicode 処理
// -----------------------------------------------------
// "\n" 3~1つ前までを確定してSYMBOLを作成する
// "\r" 3~1つ前までを確定してSYMBOLを作成する
// '\u0009' 3~1つ前までを確定してSYMBOLを作成する
// '\u0020' 3~1つ前までを確定してSYMBOLを作成する
// その他 SYMBOLを作成する(<?を作って残りはPITargetへ)
// EOF 3~1つ前までを確定してSYMBOLを作成する
//
// 処理1:3~1つ前までを確定してSYMBOLを作成する
// 処理2:SYMBOLを作成する(<?を作って残りはPITargetへ)
//
UpdateRange_MovePos proc1
= new UpdateRange_MovePos(m_symbolMaker, 3,1);
// proc2 = m_symbolMaker
LexicalMap map = new LexicalMap();
map.put('\n' , proc1);
map.put('\r' , proc1);
map.put('\u0009', proc1);
map.put('\u0020', proc1);
map.setOutOfRange(m_symbolMaker);
map.setEofFunctor(proc1);
m_mapList.add(map);
}
private void makeMap_F()
{
//
//表 F <が確定して、1つ前が!で未確定の状態
// 記号 処理
// -----------------------------------------------------
// "[" 1つ前と今の文字を確定して、
// 続きがCDATA[ならSymbolを作成する
// 違うなら<![を作成する
// "-" 今の文字を確定せずにGへ
// その他 続きが、ATTLIST,DOCTYPE,ENTITY,
// ELEMENT,NOTATIONの場合は
// それらを確定させて、SYMBOLを作成する。
// 違うなら<でSYMBOLを作成する
// EOF SYMBOLを作成する
//
// 処理1:1つ前と今の文字を確定して、
// 続きがCDATA[ならSYMBOLを作成する
// 違うなら<![を作成する
// 処理2:今の文字を確定せずにGへ
// 処理3:続きが、ATTLIST,DOCTYPE,ENTITY,ELEMENT,
// NOTATIONの場合はそれらを確定させて、
// SYMBOLを作成する。違うなら<でSYMBOLを作成する
// 処理4:SYMBOLを作成する
//
UpdateRange_MovePos proc1
= new UpdateRange_MovePos(m_leftBracket_Tokenizer, 1,0);
MovePos_MoveMap proc2 = new MovePos_MoveMap(6);
DtdSymbolTokenMaker proc3 = new DtdSymbolTokenMaker();
// proc4 = m_symbolMaker
LexicalMap map = new LexicalMap();
map.put('[' , proc1);
map.put('-' , proc2);
map.setOutOfRange(proc3);
map.setEofFunctor(m_symbolMaker);
m_mapList.add(map);
}
private void makeMap_G()
{
//表 G <が確定して、2つ前が!、1つ前が- で未確定の状態
// 記号 処理
// -----------------------------------------------------
// "-" 2つ前から今の文字を確定して、CommentTokenizerへ
// その他 SYMBOLを作成する
// EOF SYMBOLを作成する
//
// 処理1:2つ前から今の文字を確定して、CommentTokenizerへ
// 処理2:SYMBOLを作成する
//
CommentTokenizer commentTokenizer = new CommentTokenizer();
UpdateRange_MovePos proc1
= new UpdateRange_MovePos(commentTokenizer, 2,0);
// proc2 = m_symbolMaker
LexicalMap map = new LexicalMap();
map.put('-' , proc1);
map.setOutOfRange(m_symbolMaker);
map.setEofFunctor(m_symbolMaker);
m_mapList.add(map);
}
}
CommentTokenizerの実装
public class CommentTokenizer extends MultipleMapsSwitchTokenizer
{
private class CommentTokenMaker implements Functor
{
public CommentTokenMaker()
{
}
@Override
public Token tokenize(StringBuilder str, int pos)
{
m_tokenBuilder.update(m_char);
pos++;
return new CommentToken();
}
}
private class BadcommentTokenMaker implements Functor
{
public BadcommentTokenMaker()
{
}
@Override
public Token tokenize(StringBuilder str, int pos)
{
return new BadCommentToken();
}
}
private final Update_MovePos_MoveMap m_umpmm_A;
private final Update_MovePos_MoveMap m_umpmm_B;
private final Update_MovePos_MoveMap m_umpmm_C;
private final BadcommentTokenMaker m_btm;
private final CommentTokenMaker m_ctm;
private final Update_MovePos m_um;
public CommentTokenizer()
{
m_umpmm_A = new Update_MovePos_MoveMap(0);
m_umpmm_B = new Update_MovePos_MoveMap(1);
m_umpmm_C = new Update_MovePos_MoveMap(2);
m_btm = new BadcommentTokenMaker();
m_ctm = new CommentTokenMaker();
m_um = new Update_MovePos(m_btm);
makeMap_A();
makeMap_B();
makeMap_C();
}
private void makeMap_A()
{
LexicalMap map = new LexicalMap();
//表A
//
//記号 UniCode 処理
//----------------------------------------------
//Char #x1-#x002C 1文字確定する。表Aへ
//'-' #x002D 1文字確定する。表Bへ
//Char #x002E-#xD7FF 1文字確定する。表Aへ
//Char #xE000-#xFFFD 1文字確定する。表Aへ
//その他 1文字確定する。BAD_COMMENT_TOKENを作成する
//EOF BAD_COMMENT_TOKENを作成する
map.put('\u0001', '\u002C', m_umpmm_A);
map.put('\u002D', m_umpmm_B);
map.put('\u002E', '\uD7FF', m_umpmm_A);
map.put('\uE000', '\uFFFD', m_umpmm_A);
map.setOutOfRange(m_um);
map.setEofFunctor(m_btm);
m_mapList.add(map);
}
private void makeMap_B()
{
LexicalMap map = new LexicalMap();
//表B
//
//記号 UniCode 処理
//----------------------------------------------
//Char #x1-#x002C 1文字確定する。表Aへ
//'-' #x002D 1文字確定する。表Cへ
//Char #x002E-#xD7FF 1文字確定する。表Aへ
//Char #xE000-#xFFFD 1文字確定する。表Aへ
//その他 1文字確定する。BAD_COMMENT_TOKENを作成する
//EOF BAD_COMMENT_TOKENを作成する
map.put('\u0001', '\u002C', m_umpmm_A);
map.put('\u002D', m_umpmm_C);
map.put('\u002E', '\uD7FF', m_umpmm_A);
map.put('\uE000', '\uFFFD', m_umpmm_A);
map.setOutOfRange(m_um);
map.setEofFunctor(m_btm);
m_mapList.add(map);
}
private void makeMap_C()
{
LexicalMap map = new LexicalMap();
//表C
//
//記号 UniCode 処理
//---------------------------------------------
//'>' #x003C 1文字確定する。COMMENT_TOKENを作成する
//その他 1文字確定する。BAD_COMMENT_TOKENを作成する
//EOF BAD_COMMENT_TOKENを作成する
map.put('>', m_ctm);
map.setOutOfRange(m_um);
map.setEofFunctor(m_btm);
m_mapList.add(map);
}
}
コメント
コメントを投稿する
SpecialPR