本家「@IT」にはない内容をエンジニアライフで技術紹介するコラム。広く議論する場になることを目指します。

第026回_条件付字句解析器2_CDataLexer_CDataTokenizer

»
今回は、CDATA_WORDの字句解析器を検討します。

CDataを字句解析するCDataLexerの検討

CDataの構文を確認した上で検討に入ります。

CDataの構文
 [20] CData ::= (Char* − (Char* ']]>' Char*))
つまり、
 CDATA_WORD ::= "]]>" を含まないCharの文字列
また、Char*とあるので0文字の場合もありえます。

1文字目における処理分岐
CDataの範囲で1文字取得した場合、どのように処理するかを検討します。

まず、CDataは "]]>"を含まない Charの文字列ですから、1文字目にCharと"]"を取得した場合とその他の場合で分岐します。

記号UniCode処理
――――――――――――――――――――――――――――――――――――――
Char#x0001-#x005C初期化して、CDataTokenizerへ移動する
]#x005D今の文字"]"を確定せず、次の文字に移動して、CDataTokenizer2へ移動する
Char#x005E-#xD7FF初期化して、CDataTokenizerへ移動する
Char#xE000-#xFFFD初期化して、CDataTokenizerへ移動する
その他空のCDataTokenを作成する
EOF空のCDataTokenを作成する

CDataLexerの実装

ここまでの検討から実装は次のようになります。


public class CDataLexer extends MapSwitchLexer
{
    private class CDataTokenMaker implements Functor
    {
        public CDataTokenMaker()
        {
        }
        
        @Override
        public Token tokenize(StringBuilder str, int pos)
        {
            m_tokenBuilder.initStart();
            return new CDataToken();
        }
    }

    private static final CDataLexer m_instance = new CDataLexer();

    private CDataLexer()
    {
        //記号        UniCode           処理
        //-----------------------------------------------------------------------------------------
        //Char        #x0001-#x005C     初期化して、CDataTokenizerへ移動する
        //"]"         #x005D            今の文字"]"を確定せず、
        //                                次の文字に移動して
        //                                CDataTokenizer2へ移動する
        //Char        #x005E-#xD7FF     初期化して、CDataTokenizerへ移動する
        //            #xE000-#xFFFD     初期化して、CDataTokenizerへ移動する
        //その他      ------            空のCDataTokenを作成する
        //EOF                           空のCDataTokenを作成する
        
        Init_MovePos    
            cDataTokenizer  = new Init_MovePos(new CDataTokenizer());
        MovePos
            cDataTokenizer2 = new MovePos(new CDataTokenizer2());
        CDataTokenMaker ctm             = new CDataTokenMaker();
        
        //0005Cはスラッシュ
        m_map.put('\u0001', '\u005C\u005C', cDataTokenizer);
        m_map.put('\u005D',                 cDataTokenizer2);
        m_map.put('\u005E', '\uD7FF',       cDataTokenizer);
        m_map.put('\uE000', '\uFFFD',       cDataTokenizer);
        m_map.setOutOfRange(ctm);
        m_map.setEofFunctor(ctm);
    }

    public static CDataLexer getInstance()
    {
        return m_instance;
    }
}

CDataTokenizerの検討

CDataTokenizerで1文字目がCharの場合に2文字目以降に対して実施するトークナイザがCDataTokenizerです。

表Aの処理(1つ前が確定している)
CDataTokenizerがスタート地点として使う表Aについて検討します。

記号UniCode処理
――――――――――――――――――――――――――――――――――――――
Char#x0001-#x005C今の文字を確定する(次の文字も表Aで処理する)
]#x005D確定せずに次の文字のために表Bへ移動する
Char#x005E-#xD7FF今の文字を確定する(次の文字も表Aで処理する)
Char#xE000-#xFFFD今の文字を確定する(次の文字も表Aで処理する)
その他CDataTokenを作成する
EOFCDataTokenを作成する

表Bの処理(一つ前が ]で未確定)
表Aで出てきた表Bについても検討すると、
記号UniCode処理
――――――――――――――――――――――――――――――――――――――
Char#x0001-#x005C1つ前と今の文字を確定する。表Aへ移動する
]#x005D確定せずに次の文字のために表Cへ移動する
Char#x005E-#xD7FF1つ前と今の文字を確定する。表Aへ移動する
Char#xE000-#xFFFD1つ前と今の文字を確定する。表Aへ移動する
その他1つ前を確定する。CDataTokenを作成する
EOF1つ前を確定する。CDataTokenを作成する
となります。

表Cの処理(2つ前が ]で未確定,1つ前が ]で未確定)
表Bで出てきた表Cについても検討すると、
記号UniCode処理
――――――――――――――――――――――――――――――――――――――
Char#x0001-#x003D2つ前、1つ前、今の文字を確定する。表Aへ移動する
>#x003ECDataTokenを作成する
Char#x003F-#xD05C2つ前、1つ前、今の文字を確定する。表Aへ移動する
]#xD05D2つ前を確定する。表Cへ移動する
Char#x005E-#xD7FF2つ前、1つ前、今の文字を確定する。表Aへ移動する
Char#xE000-#xFFFD2つ前、1つ前、今の文字を確定する。表Aへ移動する
その他2つ前、1つ前を確定する。CDataTokenを作成する
EOF2つ前、1つ前を確定する。CDataTokenを作成する
となります。

CDataTokenizerの実装

先ほどの検討を元にCDataTokenizerを実装します。

public class CDataTokenizer extends MultipleMapsSwitchTokenizer
{

    private class CDataTokenMaker implements Functor
    {
        public CDataTokenMaker()
        {
        }
        
        @Override
        public Token tokenize(StringBuilder str, int pos)
        {
            return new CDataToken();
        }
    }
    
    protected final CDataTokenMaker m_ctm;

    public CDataTokenizer()
    {
        m_ctm  = new CDataTokenMaker();

        makeMap_A();
        makeMap_B();
        makeMap_C();
    }
    
    private void makeMap_A()
    {
        LexicalMap map = new LexicalMap();

        //表Aの処理(1つ前が確定している)
        //次の文字   UniCode         処理
        //-----------------------------------------------------------------------------------------
        //Char       #x0001-#x005C   今の文字を確定する
        //                           (次の文字も表Aで処理する)
        //"]"        #x005D          確定せずに次の文字のために
        //                           表Bへ移動する
        //Char       #x005E-#xD7FF   今の文字を確定する
        //                           (次の文字も表Aで処理する)
        //           #xE000-#xFFFD   今の文字を確定する
        //                           (次の文字も表Aで処理する)
        //その他     ------          CDataTokenを作成する
        //EOF                        CDataTokenを作成する
        Update_MovePos    proc1 = new Update_MovePos(this);
        MovePos_MoveMap proc2 = new MovePos_MoveMap(1);
        
        map.put('\u0001', '\u005C\u005C', proc1);
        map.put('\u005D',           proc2);
        map.put('\u005E', '\uD7FF', proc1);
        map.put('\uE000', '\uFFFD', proc1);
        map.setOutOfRange(m_ctm);
        
        //EOFの場合
        map.setEofFunctor(m_ctm);

        m_mapList.add(map);
    }
    
    private void makeMap_B()
    {
        LexicalMap map = new LexicalMap();
        

        //表Bの処理(一つ前が ]で未確定)
        //次の文字   UniCode         処理
        //-----------------------------------------------------------------------------------------
        //Char       #x0001-#x005C   1つ前と今の文字を確定する。
        //                             表Aへ移動する
        //"]"        #x005D          確定せずに次の文字のために
        //                             表Cへ移動する
        //Char       #x005E-#xD7FF   1つ前と今の文字を確定する。
        //                             表Aへ移動する
        //           #xE000-#xFFFD   1つ前と今の文字を確定する。
        //                             表Aへ移動する
        //その他     ------          1つ前を確定する。CDataTokenを作成する
        //EOF                        1つ前を確定する。CDataTokenを作成する
        UpdateRange_MovePos_MoveMap
                            proc1 = new UpdateRange_MovePos_MoveMap(0,1,0);
        MovePos_MoveMap     proc2 = new MovePos_MoveMap(2);
        UpdateRange_MovePos proc3 = new UpdateRange_MovePos(m_ctm,1,1);

        map.put('\u0001', '\u005C\u005C', proc1);
        map.put('\u005D',                 proc2);
        map.put('\u005E', '\uD7FF',       proc1);
        map.put('\uE000', '\uFFFD',       proc1);
        map.setOutOfRange(proc3);
        map.setEofFunctor(proc3);
        m_mapList.add(map);
    }
    
    private void makeMap_C()
    {
        LexicalMap map = new LexicalMap();

        //表Cの処理(2つ前が ]で未確定,1つ前が ]で未確定)
        //次の文字   UniCode         処理
        //-----------------------------------------------------------------------------------------
        //Char       #x0001-#x003D   2つ前、1つ前、今の文字を確定する。
        //                             表Aへ移動する
        //">"        #x003E          CDataTokenを作成する
        //Char       #x003F-#x005C   2つ前、1つ前、今の文字を確定する。
        //                             表Aへ移動する
        //"]"        #x005D          2つ前を確定する。表Cへ移動する
        //Char       #x005E-#xD7FF   2つ前、1つ前、今の文字を確定する。
        //                             表Aへ移動する
        //           #xE000-#xFFFD   2つ前、1つ前、今の文字を確定する。
        //                             表Aへ移動する
        //その他     ------          2つ前、1つ前を確定する。
        //                             CDataTokenを作成する
        //EOF                        2つ前、1つ前を確定する。
        //                             CDataTokenを作成する
        UpdateRange_MovePos_MoveMap
                           proc1 = new UpdateRange_MovePos_MoveMap(0,2,0);
        //                 proc2 = m_ctm
        UpdateRange_MovePos_MoveMap
                           proc3 = new UpdateRange_MovePos_MoveMap(2,2,2);
        UpdateRange_MovePos  proc4 = new UpdateRange_MovePos(m_ctm,2,1);
        
        map.put('\u0001', '\u003D', proc1);
        map.put('\u003E',           m_ctm);
        map.put('\u003F', '\u005C\u005C', proc1);
        map.put('\u005D',           proc3);
        map.put('\u005E', '\uD7FF', proc1);
        map.put('\uE000', '\uFFFD', proc1);
        map.setOutOfRange(proc4);
        map.setEofFunctor(proc4);

        m_mapList.add(map);
    }
}

CDataTokenizer2の検討

CDataLexerで1文字目が"]"の場合に、未初期化で2文字目以降に対して実施するトークナイザがCDataTokenizer2です。

表Dの処理(初期化していない、1つ前が]で確定していない)
記号UniCode処理
――――――――――――――――――――――――――――――――――――――
Char#x0001-#x005C初期化する。1つ前と今の文字を確定させて表Aへ移動する
]#xD05D確定せずに表Eへ移動する
Char#x005E-#xD7FF初期化する。1つ前と今の文字を確定させて表Aへ移動する
Char#xE000-#xFFFD初期化する。1つ前と今の文字を確定させて表Aへ移動する
その他初期化する。1つ前を確定する。CDataTokenを作成する
EOF初期化する。1つ前を確定する。CDataTokenを作成する

表Eの処理(初期化していない、2つ前が]で確定していない、1つ前が]で確定していない)
記号UniCode処理
――――――――――――――――――――――――――――――――――――――
Char#x0001-#x003D初期化する。2つ前、1つ前、今の文字を確定する。表Aへ移動する
>#x003ECDataTokenを作成する
Char#x003F-#xD05C初期化する。2つ前、1つ前、今の文字を確定する。表Aへ移動する
]#xD05D初期化する。2つ前を確定する。表Cへ移動する
Char#x005E-#xD7FF初期化する。2つ前、1つ前、今の文字を確定する。表Aへ移動する
Char#xE000-#xFFFD初期化する。2つ前、1つ前、今の文字を確定する。表Aへ移動する
その他初期化する。2つ前、1つ前を確定する。CDataTokenを作成する
EOF初期化する。2つ前、1つ前を確定する。CDataTokenを作成する

継承関係
CDataTokenizer2は表A(表Aの内部では表Bにも移動する)、表Cに移動する可能性があるためCDataTokenizerを継承します。

CDataTokenizer2の実装

先の検討から実装は次のようになります。

public class CDataTokenizer2 extends CDataTokenizer
{
    private class EmptyCDataTokenMaker implements Functor
    {
        public EmptyCDataTokenMaker()
        {
        }
        
        @Override
        public Token tokenize(StringBuilder str, int pos)
        {
            m_tokenBuilder.initStart();
            return new CDataToken();
        }
    }

    
    private final EmptyCDataTokenMaker    m_ectm;

    public CDataTokenizer2()
    {
        super();
        m_startIndex= 3;//(Dのmapから始める)
        m_currentIndex = 3;
        
        m_ectm   = new EmptyCDataTokenMaker();
        
        makeMap_D();
        makeMap_E();
    }
    
    private void makeMap_D()
    {
        LexicalMap map = new LexicalMap();

        //表Dの処理(初期化していない、1つ前が]で確定していない)
        //次の文字   UniCode         処理
        //-----------------------------------------------------------------------------------------
        //Char       #x0001-#x005C   1初期化する。1つ前と今の文字を確定させて
        //                             表Aへ移動する
        //"]"        #x005D          確定せずに表Eへ移動する
        //Char       #x005E-#xD7FF   初期化する。1つ前と今の文字を確定させて
        //                             表Aへ移動する
        //           #xE000-#xFFFD   初期化する。1つ前と今の文字を確定させて
        //                             表Aへ移動する
        //その他     ------          初期化する。1つ前を確定する。
        //                             CDataTokenを作成する
        //EOF                        初期化する。1つ前を確定する。
        //                             CDataTokenを作成する
        
        InitUpdateRange_MovePos_MoveMap 
                    proc1 = new InitUpdateRange_MovePos_MoveMap(0,1,0);
        MovePos_MoveMap  
                    proc2 = new MovePos_MoveMap(4);
        InitUpdateRange_MovePos
                    proc3 = new InitUpdateRange_MovePos(m_ctm,1,1);

        map.put('\u0001', '\u005C\u005C', proc1);
        map.put('\u005D',                 proc2 );
        map.put('\u005E', '\uD7FF',       proc1);
        map.put('\uE000', '\uFFFD',       proc1);
        map.setOutOfRange(proc3);
        map.setEofFunctor(proc3);
        
        m_mapList.add(map);
    }
    
    private void makeMap_E()
    {
        LexicalMap map = new LexicalMap();
        
        //表Eの処理(初期化していない、2つ前が]で確定していない、1つ前が]で確定していない)
        //次の文字   UniCode         処理
        //-----------------------------------------------------------------------------------------
        //Char       #x0001-#x003D   初期化する。2つ前、1つ前、
        //                             今の文字を確定する。
        //                             表Aへ移動する
        //">"        #x003E          CDataTokenを作成する
        //Char       #x003F-#x005C   初期化する。2つ前、1つ前、
        //                             今の文字を確定する。
        //                             表Aへ移動する
        //"]"        #x005D          初期化する。2つ前を確定する。
        //                             表Cへ移動する
        //Char       #x005E-#xD7FF   初期化する。2つ前、1つ前、
        //                             今の文字を確定する。
        //                             表Aへ移動する
        //           #xE000-#xFFFD   初期化する。2つ前、1つ前、
        //                             今の文字を確定する。
        //                             表Aへ移動する
        //その他     ------          初期化する。2つ前、1つ前を確定する。
        //                             CDataTokenを作成する
        //EOF                        初期化する。2つ前、1つ前を確定する。
        //                             CDataTokenを作成する
        
        InitUpdateRange_MovePos_MoveMap
                    proc1 = new InitUpdateRange_MovePos_MoveMap(0,2,0);
        //                              proc2 = m_ectm
        InitUpdateRange_MovePos_MoveMap
                    proc3 = new InitUpdateRange_MovePos_MoveMap(2,2,2);
        InitUpdateRange_MovePos
                    proc4 = new InitUpdateRange_MovePos(m_ctm,2,1);
        
        
        map.put('\u0001', '\u003D',       proc1);
        map.put('\u003E',                 m_ectm);
        map.put('\u003F', '\u005C\u005C', proc1);
        map.put('\u005D',                 proc3);
        map.put('\u005E', '\uD7FF',       proc1);
        map.put('\uE000', '\uFFFD',       proc1);
        map.setOutOfRange(proc4);
        map.setEofFunctor(proc4);
        
        m_mapList.add(map);
    }
}
Comment(0)

コメント

コメントを投稿する