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

第020回_TokenManagerの実装2_notify系の追加

»

前回に引き続きTokenManagerの実装を行います。

4.条件付字句解析を適切に呼び出す機能

条件付字句解析器の各条件による切り替えの呼び出しタイミングを制御するのはVisitor側の責任です。VisitorはTokenManagerに現在の解析進行状況について通知することでタイミングの制御をします。 20クラス構成図.jpg
20-1.ソフトウェア構成図

Visitor側の呼び出し例
 Visitor側の実装は次のようになります。

class Visitor
{
    ・・・・

    private static TokenManager m_tokenManager = TokenManager.getInstance();

    ・・・・

    public void visit(EbnfPIWordNode n)
    {
        //PI_WORDの条件の開始制御
        m_tokenManager.notifyPIWordStart();

        //この区間でnextToken()を呼び出しときは、
        //PI_WORD用の条件付字句解析を呼び出す
        Token token = m_tokenManager.nextToken();
        m_result = token.match(n);

        //PI_WORDの条件の終了制御
        m_tokenManager.notifyPIWordEnd();
    }
}

notify系の実装
例を見てわかる通り、TokenManagerは制御インタフェース
 TokenManager.notify$XXXX$Start と TokenManager.notify$XXXX$End
を提供します。
# $XXXX$ 部分は条件名

TokenManager#nextTokenは切り替え指示範囲に応じた適切な条件付字句解析器を呼び出して、範囲の条件に合わせた正しい字句を抽出するものとします。
次に、TokenManagerが提供するnotify$XXXX$Startとnotify$XXXX$Endの処理を考えていきます。

notify$XXXX$Startの処理1
notify$XXXX$Startで解析に必要な字句解析器をTokenManagerに登録します。
登録する字句解析器は
 01.CHARDATA_WORDの字句解析器
 02.PI_WORDの字句解析器
 03.CDATA_WORDの字句解析器
 04.IGNORE_WORDの字句解析器
 05.AttValue/EntityValueの開始を示すDoubleQouteを解析する字句解析器
 06.AttValue/EntityValueの開始を示すQouteを解析する字句解析器
 07.AttValueDoubleQuoteSectionの字句解析器
 08.AttValueQuoteSectionの字句解析器
 09.EntityValueDoubleQuoteSectionの字句解析器
 10.EntityValueQuoteSectionの字句解析器
 11.NormalSectionの字句解析器
となります。
また、
 ・EntityRefSection
 ・CharRefDecimalSection
 ・CharRefHexSection
 ・PEReferenceSection
はnotifyは必要ですが、字句解析器はNormalSectionとなります。

字句解析器同士の遷移の構造
次に字句解析器の遷移について考えてみます。

・1段階の遷移
 Aの字句解析器
  notify$XXX$Start
  →XXXの字句解析器
  notify$XXX$End
 →Aの字句解析器

・2段階の遷移
 AAAの字句解析器
  notify$XXX$Start
  →XXXの字句解析器
   notify$BBB$Start
   →BBBの字句解析器
   notify$BBB$End
  →XXXの字句解析器
  notify$XXX$End
 →AAAの字句解析器

・3段階の遷移
 AAAの字句解析器
  notify$XXX$Start
  →XXXの字句解析器
   notify$BBB$Start
   →BBBの字句解析器
    notify$YYY$Start
    →YYYの字句解析器
    notify$YYY$End
   →BBBの字句解析器
   notify$BBB$End
  →XXXの字句解析器
  notify$XXX$End
 →AAAの字句解析器

このように字句解析器の遷移はスタックのように遷移するため状態を管理せずに解析器自体をスタックに積んで使うのが良いでしょう。

notify$XXXX$Startの処理2
トークンバッファに解析中のトークンがある場合のためにトークンバッファは空にリセットします。

notify$XXXX$Endの処理
notify$XXXX$Startで追加した字句解析器をスタックから取り除きます。
またトークンバッファに解析中のトークンがある場合のためにトークンバッファは空にリセットします。

必要なデータ
字句解析器を保存するスタックをフィールドに用意して、コンストラクタで 初期化します。

TokenManagerにnotify系を実装する

public class TokenManager
{
    ・・・・・

    //追加
    private BaseStackLexer m_stack;

    //シングルトンパターン
    private TokenManager()
    {
       ・・・・・

        //追加
        m_stack = StackLexer.getInstance();

       ・・・・・
    }

    //追加
    //字句解析器制御メソッド
    public void notifyCDataStart()
    {
        m_stack.pushCData();
        m_list.clear();
    }

    public void notifyCDataEnd()
    {
        m_stack.pop();
        m_list.clear();
    } 

    public void notifyPIWordStart()
    {
        m_stack.pushPI();
        m_list.clear();
    }
    public void notifyPIWordEnd()
    {
        m_stack.pop();
        m_list.clear();
    } 

    public void notifyCharDataStart()
    {
        m_stack.pushCharData(); 
        m_list.clear();
    }

    public void notifyCharDataEnd()
    {
        m_stack.pop();
        m_list.clear();
    } 

    public void notifyIgnoreWordStart()
    {
        m_stack.pushIgnore();
        m_list.clear();
    }

    public void notifyIgnoreWordEnd()
    {
        m_stack.pop();
        m_list.clear();
    } 

    public void notifyQuoteStart()
    {
        m_stack.pushQuote();
        m_list.clear();
    }

    public void notifyQuoteEnd()
    {
        m_stack.pop();
        m_list.clear();
    } 

    public void notifyDoubleQuoteStart()
    {
        m_stack.pushDoubleQuote();
        m_list.clear();
    }

    public void notifyDoubleQuoteEnd()
    {
        m_stack.pop();
        m_list.clear();
    } 

    public void notifyAttValueQuoteSectionStart()
    {
        m_stack.pushAttValueQuoteSection();
        m_list.clear();
    }

    public void notifyAttValueQuoteSectionEnd()
    {
        m_stack.pop();
        m_list.clear();
    } 

    public void notifyAttValueDoubleQuoteSectionStart()
    {
        m_stack.pushAttValueDoubleQuoteSection();
        m_list.clear();
    }
    public void notifyAttValueDoubleQuoteSectionEnd()
    {
        m_stack.pop();
        m_list.clear();
    } 

    public void notifyEntityValueQuoteSectionStart()
    {
        m_stack.pushEntityValueQuoteSection();
        m_list.clear();
    }

    public void notifyEntityValueQuoteSectionEnd()
    {
        m_stack.pop();
        m_list.clear();
    } 

    public void notifyEntityValueDoubleQuoteSectionStart()
    {
        m_stack.pushEntityValueDoubleQuoteSection();
        m_list.clear();
    }

    public void notifyEntityValueDoubleQuoteSectionEnd()
    {
        m_stack.pop();
        m_list.clear();
    } 

    public void notifyNormalSectionStart()
    {
        m_stack.pushNormalSection();
        m_list.clear();
    }

    public void notifyNormalSectionEnd()
    {
        m_stack.pop();
        m_list.clear();
    } 

    ・・・・
}

BaseStackLexerの実装
字句解析器のスタックを用意します。

public class BaseStackLexer
{
    private final NormalSectionLexer m_normalSectionLexer
                                    = NormalSectionLexer.getInstance();
    private final PISectionLexer m_piSectionLexer
                                    = PISectionLexer.getInstance();
    private final CharDataLexer m_charDataLexer
                                    = CharDataLexer.getInstance();
    private final CDataLexer m_cDataLexer
                                    = CDataLexer.getInstance();
    private final IgnoreLexer m_ignoreLexer
                                    = IgnoreLexer.getInstance();
    private final ReplacementTextLexer m_replacementTextLexer
                                    = ReplacementTextLexer.getInstance();
    private final ReplacementTextLexer2 m_replacementTextLexer2
                                    = ReplacementTextLexer2.getInstance();
    private final QuoteLexer m_quoteLexer
                                    = QuoteLexer.getInstance();
    private final DoubleQuoteLexer m_doubleQuoteLexer
                                    = DoubleQuoteLexer.getInstance();
    
    private final AttValueQuoteSectionLexer m_attValueQuoteSectionLexer
                                = AttValueQuoteSectionLexer.getInstance();
    private final AttValueDoubleQuoteSectionLexer 
                         m_attValueDoubleQuoteSectionLexer
                          = AttValueDoubleQuoteSectionLexer.getInstance();
    private final EntityValueQuoteSectionLexer
                        m_geEntityValueQuoteSectionLexer
                          = GeEntityValueQuoteSectionLexer.getInstance();
    private final EntityValueDoubleQuoteSectionLexer
                     m_geEntityValueDoubleQuoteSectionLexer
                       = GeEntityValueDoubleQuoteSectionLexer.getInstance();
    
    private Stack<ConditionLexer> m_stack;

    //シングルトンパターン
    public BaseStackLexer()
    {
        m_stack = new Stack<ConditionLexer>();
        
        m_stack.push(m_normalSectionLexer);
    }
    
    public void pushPI()
    {m_stack.push(m_piSectionLexer);}

    public void pushCharData()
    {m_stack.push(m_charDataLexer);}

    public void pushCData()
    {m_stack.push(m_cDataLexer);}

    public void pushIgnore()
    {m_stack.push(m_ignoreLexer);}

    public void pushNormal()
    {m_stack.push(m_normalSectionLexer);}
    
    public ConditionLexer peek()
    {return m_stack.peek();}
    public void pop()
    {m_stack.pop();}

    public void pushQuoteStart()
    {m_stack.push(m_quoteLexer);}
    public void pushDoubleQuoteStart()
    {m_stack.push(m_doubleQuoteLexer);}

    public void pushAttValueQuoteSection()
    {m_stack.push(m_attValueQuoteSectionLexer);}
    public void pushAttValueDoubleQuoteSection()
    {m_stack.push(m_attValueDoubleQuoteSectionLexer);}

    public void pushEntityValueQuoteSection()
    {m_stack.push(m_geEntityValueQuoteSectionLexer);}
    public void pushEntityValueDoubleQuoteSection()
    {m_stack.push(m_geEntityValueDoubleQuoteSectionLexer);}
}

tokenize()の実装
字句解析器を選択する処理を実装したので、前回nextToken()の中で保留していたtokenize()を実装します。

public class TokenManager
{
    ・・・・・
    //追加で解析してlistを補充する
    private void tokenize()
    {
        ConditionLexer lexer =  m_stack.peek();

        Token token = lexer.nextToken(m_str);
        m_list.add(token);
    }
}
Comment(0)

コメント

コメントを投稿する