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

第028回_条件付字句解析器4_AttValue系Lexer_JustCreateSymbolTokenizer他

»
コラムニストの中で「いいね!」の数が底辺の相転移炉です。こんにちは。
ソースコードを載せてるだけで分析が少ないので手抜き回に見えるかも・・・と思いつつ、第032回まで(8月28日分まで)この流れが続きます。
今がダメに見えても後で見返すときに見やすいはず...と筆者は思っていますが、ご意見・ご感想がある場合は参考にさせていただきますのでお気軽にコメントしていただけたらと思います。
それでは本文へ。


今回は、AttValueを字句解析する字句解析器を検討します。

字句解析器が必要な部分の洗い出し

第007回の内容から、
 AttValue ::= AttValueDoubleQuote | AttValueQuote
 AttValueDoubleQuote ::= DoubleQuoteStart AttValueDoubleQuoteSection
 DoubleQuoteStart ::= '"'
 AttValueDoubleQuoteSection ::= (NORMAL_TOKEN_A1 | Reference)* '"'
 AttValueQuote ::= QuoteStart AttValueQuoteSection
 Quote ::= "'"
 AttValueQuoteSection ::= (NORMAL_TOKEN_A2 | Reference)* "'"
と構文を整理し、
No.条件トークン一覧
―――――――――――――――――――――――――――――――――――――
05DoubleQuoteを構文解析している SYMBOL ('"')
06Quoteを構文解析している SYMBOL ("'")
07AttValueDoubleQouteSectionを構文解析しているNORMAL_TOKEN_A1,
SYMBOL ('&'),
SYMBOL ('&#')
SYMBOL ('&#x')
08AttValueQouteSectionを構文解析している NORMAL_TOKEN_A2
SYMBOL ('&'),
SYMBOL ('&#')
SYMBOL ('&#x')
を分類しましたので、DoubleQuoteLexer,QuoteLexer,AttValueDoubleQuoteSectionLexer,AttValueQuoteSectionLexerを実装します。

DoubleQuoteLexerの実装


public class DoubleQuoteLexer extends MapSwitchLexer
{
    private static final DoubleQuoteLexer 
                    m_instance = new DoubleQuoteLexer();

    private DoubleQuoteLexer()
    {
        //次の文字    処理
        //-----------------------------------------------------------------------------------------
        //'"'         初期化して、"だけのSymbolTokenを作成する
        //その他      初期化して、残りの文字列をすべてを使って
        //              UnknownTokenを作成する
        //EOF         EofTokenを作成する
        Init_MovePos
          symbol = new Init_MovePos(new JustCreateSymbolTokenizer());
        Init_MovePos
          unknown = new Init_MovePos(new UnknownTokenizer());
        EofTokenizer eof     = new EofTokenizer();
        
        m_map.put('"',  symbol);
        m_map.setOutOfRange(unknown);
        m_map.setEofFunctor(eof);
    }

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

DoubleQuoteLexerで必要なTokenizerの実装

DoubleQuoteLexerで出現した未解説のクラスについて紹介しておきます。中身は単純ですので細かい説明をしなくても良いと思います。
JustCreateSymbolTokenizer

public class JustCreateSymbolTokenizer extends TermTokenizer
{
    public JustCreateSymbolTokenizer(){}

    @Override
    public Token tokenize(StringBuilder str, int pos)
    {
        return new SymbolToken();
    }
}

UnknownTokenizer

public class UnknownTokenizer extends TermTokenizer
{
    public UnknownTokenizer(){}

    @Override
    public Token tokenize(StringBuilder str, int pos)
    {
        //バッファの残りすべてをトークンとする
        m_tokenBuilder.update(str.substring(pos));
        return new UnknownToken();
    }
}

EofTokenizer

public class EofTokenizer extends TermTokenizer
{
    public EofTokenizer(){}

    @Override
    public Token tokenize(StringBuilder str, int pos)
    {
        return new EofToken();
    }
}

QuoteLexerの実装


public class QuoteLexer extends MapSwitchLexer
{
    private static final QuoteLexer m_instance = new QuoteLexer();

    private QuoteLexer()
    {
        //次の文字   処理
        //-----------------------------------------------------------------------------------------
        //"'"        初期化して、'だけのSymbolTokenを作成する
        //その他     初期化して、残りの文字列をすべてを使って
        //             UnknownTokenを作成する
        //EOF        EofTokenを作成する
        Init_MovePos
          symbol  = new Init_MovePos(new JustCreateSymbolTokenizer());
        Init_MovePos
          unknown = new Init_MovePos(new UnknownTokenizer());
        EofTokenizer     eof     = new EofTokenizer();
        
        m_map.put('\'', symbol);
        m_map.setOutOfRange(unknown);
        m_map.setEofFunctor(eof);
    }

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

AttValueDoubleQuoteSectionLexerの実装


public class AttValueDoubleQuoteSectionLexer extends MapSwitchLexer
{
    private static final AttValueDoubleQuoteSectionLexer
          m_instance = new AttValueDoubleQuoteSectionLexer();
    
    private AttValueDoubleQuoteSectionLexer()
    {
        super();
        
        //記号   処理
        //---------------------------------------------------------------
        //'"'    初期化して、JustCreateSymbolTokenizerへ移動する
        //'&'    初期化して、SymbolListTokenizer2(#x,#)へ移動する
        //'<'    初期化して、UnknownTokenizerへ移動する
        //その他 初期化して、NormalTokenA1Tokenizerへ移動する
        //EOF    EOFを作成する
        
        Init_MovePos oneCharSymbol
                   = new Init_MovePos(new JustCreateSymbolTokenizer());
        Init_MovePos normalTokenA1Tokenizer
                   = new Init_MovePos(new NormalTokenA1Tokenizer());

        ArrayList<String> list = new ArrayList<String>();
        list.add("#x");// 先に#xを評価しないと#でOKになってしまうので注意
        list.add("#");
        Init_MovePos  ampersandTokenizer
                   = new Init_MovePos(new SymbolListTokenizer2(list));
        Init_MovePos  unknownTokenizer
                   = new Init_MovePos(new UnknownTokenizer());
        EofTokenizer eof            = new EofTokenizer();

        m_map.put('"'      , oneCharSymbol);
        m_map.put('&'      , ampersandTokenizer);
        m_map.put('<'      , unknownTokenizer);
        m_map.setOutOfRange(normalTokenA1Tokenizer);
        m_map.setEofFunctor(eof);
    }

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

SymbolListTokenizer系の実装

AttValueDoubleQuoteSectionLexerで出現した未解説のクラスSymbolListTokenizer2について紹介しておきます。

SymbolListTokenizer2は、指定した文字列リストのいずれかに合致する場合は1文字目に続いてそれらを含めてSymbolTokenを生成します。もしリストのいずれの文字列にも合致しなかった場合、最初の1文字だけのSymbolTokenを作成します。

ここには出てきませんが、SymbolListTokenizerはもしリストのいずれの文字列にも合致しなかった場合UnkwnownTokenを作成します。

SymbolListTokenizerとSymbolListTokenizer2はよく似ているので共通の親クラスAbstractSymbolListTokenizerを作成します。

AbstractSymbolTokenizer
 この処理はMapではなくif文の分岐で処理しています。

public abstract class AbstractSymbolListTokenizer extends TermTokenizer
{
    private final ArrayList<String> m_targetList;

    public AbstractSymbolListTokenizer(ArrayList<String> targetList)
    {
        m_targetList = targetList;
    }

    @Override
    public Token tokenize(StringBuilder str, int pos)
    {
        Token retval = null;

        for(String target : m_targetList)
        {
            //Symbolの文字列の末尾位置+1を取得する
            int endIndex = pos + target.length();

            //対象バッファの残り文字数を取得する
            int len = str.length();

            //Symbolの文字長が残り文字数以下の場合
            if (endIndex <= len)
            {
                //対象分の文字を取得する
                //memo
                //  取得するのはendIndex-1まで
                String sub = str.substring(pos, endIndex);

                //対象文字と一致する場合
                if (sub.equals(target))
                {
                    m_tokenBuilder.update(sub);
                    retval = new SymbolToken();

                    //ループを終了する
                    break;
                }
                //対象文字と一致しない場合
                else
                {
                    //何もしない
                }
            }
            //Symbolの文字長が残り文字数より長い場合
            else
            {
                //何もしない
            }
        }

        //一致するものがなかった場合
        if (retval == null)
        {
            retval = makeTokenIfUnmatch();
        }

        return retval;
    }

    protected abstract Token makeTokenIfUnmatch();
}

SymbolListTokenizer

public class SymbolListTokenizer extends AbstractSymbolListTokenizer
{
    public SymbolListTokenizer(ArrayList<String> target)
    {
        super(target);
    }

    protected Token makeTokenIfUnmatch()
    {
        return new UnknownToken();
    }
}

SymbolListTokenizer2

public class SymbolListTokenizer2 extends AbstractSymbolListTokenizer
{
    public SymbolListTokenizer2(ArrayList<String> target)
    {
        super(target);
    }

    protected Token makeTokenIfUnmatch()
    {
        return new SymbolToken();
    }
}

NormalTokenA1Tokenizerの実装

実装はNormalTokenA1Tokenizerと後述するNormalTokenA2Tokenizerでほぼ同じなので、共通部分を抽象クラスNormalTokenATokenizerとして用意します。

NormalTokenATokenizer

public abstract class NormalTokenATokenizer extends MapSwitchTokenizer
{
    private class NormalTokenMaker implements Functor
    {
        public NormalTokenMaker(){}
        
        @Override
        public Token tokenize(StringBuilder str, int pos)
        {
            return makeToken();
        }
    }

    public NormalTokenATokenizer(char endChar)
    {
        super();
        
        NormalTokenMaker ntm = new NormalTokenMaker();
        Update_MovePos   rm  = new Update_MovePos();
        
        //記号     処理
        //----------------------------------------------
        //endChar  今の文字を含めずに、NORMAL_TOKEN_Aを作成する
        //'<'      今の文字を含めずに、NORMAL_TOKEN_Aを作成する
        //'&'      今の文字を含めずに、NORMAL_TOKEN_Aを作成する
        //その他   今の文字を確定して、次へ
        //EOF      NORMAL_TOKEN_Aを作成する
        //
        m_map.put(endChar , ntm);
        m_map.put('<'     , ntm);
        m_map.put('&'     , ntm);
        m_map.setOutOfRange(rm);
        m_map.setEofFunctor(ntm);
    }

    protected abstract Token makeToken();
}

NormalTokenA1Tokenizer

public class NormalTokenA1Tokenizer extends NormalTokenATokenizer
{
    public NormalTokenA1Tokenizer()
    {
        super('\"');
    }

    protected Token makeToken()
    {
        return new NormalTokenA1Token();
    }
}

AttValueQuoteSectionLexerの実装


public class AttValueQuoteSectionLexer extends MapSwitchLexer
{
    private static final AttValueQuoteSectionLexer
              m_instance = new AttValueQuoteSectionLexer();

    private AttValueQuoteSectionLexer()
    {
        super();
        
        //記号   UniCode     処理
        //-----------------------------------------------------------------------------------------
        //"'"               初期化して、JustCreateSymbolTokenizerへ移動する
        //'&'                初期化して、SymbolListTokenizer2(#x,#)へ移動する
        //'<'                初期化して、UnknownTokenizerへ移動する
        //' '    #xu0009     初期化して、WhiteSpaceTokenizerへ移動する
        //'\r'               初期化して、WhiteSpaceTokenizerへ移動する
        //'\n'               初期化して、WhiteSpaceTokenizerへ移動する
        //' '    #xu0020     初期化して、WhiteSpaceTokenizerへ移動する
        //その他 ------      初期化して、NormalTokenA2Tokenizerへ移動する
        //EOF                EOFを作成する
        Init_MovePos oneCharSymbol
                     = new Init_MovePos(new JustCreateSymbolTokenizer());
        Init_MovePos normalTokenA2Tokenizer
                     = new Init_MovePos(new NormalTokenA2Tokenizer());

        ArrayList<String> list = new ArrayList<String>();
        list.add("#x");// 先に#xを評価させないと#でOKになってしまうので注意
        list.add("#");
        Init_MovePos ampersandTokenizer
                     = new Init_MovePos(new SymbolListTokenizer2(list));
        Init_MovePos whiteSpaceTokenizer
                     = new Init_MovePos(new WhiteSpaceTokenizer());
        Init_MovePos unknownTokenizer
                     = new Init_MovePos(new UnknownTokenizer());
        EofTokenizer eof            = new EofTokenizer();

        
        
        m_map.put('\''     , oneCharSymbol);
        m_map.put('&'      , ampersandTokenizer);
        m_map.put('<'      , unknownTokenizer);
        m_map.put('\u0009' , whiteSpaceTokenizer);
        m_map.put('\r'     , whiteSpaceTokenizer);
        m_map.put('\n'     , whiteSpaceTokenizer);
        m_map.put('\u0020' , whiteSpaceTokenizer);
        m_map.setOutOfRange(normalTokenA2Tokenizer);
        m_map.setEofFunctor(eof);
    }

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

NormalTokenA2Tokenizerの実装


public class NormalTokenA2Tokenizer extends NormalTokenATokenizer
{
    public NormalTokenA2Tokenizer()
    {
        super('\'');
    }

    protected Token makeToken()
    {
        return new NormalTokenA2Token();
    }
}
Comment(0)

コメント

コメントを投稿する