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

第030回_条件付字句解析器6_NormalSectionLexer

»
今回は通常(NormalSection)の構文解析器 NormalSectionLexerについて検討します。

NormalSectionLexerの検討

NormalSectionは、WHITE_SPACE,WORD,SYMBOL,LITERAL,COMMENTをトークン化します。

NormalSectionで扱う各トークンの定義のおさらい
WHITE_SPACE ::= (#x20 | #x9 | #xD | #xA)+
WORD ::= (":" | [A−Z] | "_" | [a−z] | [#xC0−#xD6] | [#xD8−#xF6]
      | [#xF8−#x2FF] | [#x370−#x37D] | [#x37F−#x1FFF]
      | [#x200C−#x200D] | [#x2070−#x218F] | [#x2C00−#x2FEF]
      | [#x3001−#xD7FF] | [#xF900−#xFDCF] | [#xFDF0−#xFFFD]
      | [#x10000−#xEFFFF] | "−" | "." | [0−9] | #xB7
      | [#x0300−#x036F] | [#x203F−#x2040]
     )+
LITERAL ::= '"' '"'以外の文字 '"' | "'" "'"以外の文字 "'"
SYMBOL ::= '#FIXED' | '#IMPLIED' | '#PCDATA' | '#REQUIRED' | '%'
     | '&#' | '&#x' | '&' | '(' | ')' | ')?' | ')*' | ')+' | '*' | '+' | ',' | '/>' | ';'
     | '=' | '<'| '</' | '<?' | '<?xml' | '<![' | '<![CDATA[' | '<!ATTLIST'
     | '<!DOCTYPE' | '<!ENTITY' | '<!ELEMENT' | '<!NOTATION' | '>'
     | '?' | '?>' | '[' | ']' | ']]>' | '|'
COMMENT ::= '<!−−' 連続した'−−'をもたない文字列 '−−>'

仕様
各トークンについて1文字目で整理すると次のようになります。
記号UniCode処理
――――――――――――――――――――――――――――――――――――――
#x0009初期化して、WhiteSpaceTokenizerへ移動する
'\n'#x000A初期化して、WhiteSpaceTokenizerへ移動する
'\r'#x000D初期化して、WhiteSpaceTokenizerへ移動する
#x0020初期化して、WhiteSpaceTokenizerへ移動する
"\""#x0022初期化して、DoubleQuoteTokenizerへ移動する
"#"#x0023初期化して、SymbolListTokenizer(FIXED,IMPLIED,PCDATA,REQUIRED)へ移動する
"%"#x0025初期化して、JustCreateSymbolTokenizerへ移動する
"&"#x0026初期化して、SymbolListTokenizer2へ移動する
"'"#x0027初期化して、QuoteTokenizerへ移動する
"("#x0028初期化して、JustCreateSymbolTokenizerへ移動する
")"#x0029初期化して、SymbolListTokenizer2へ移動する
"*+,"#x002A-#x002C初期化して、JustCreateSymbolTokenizerへ移動する
"-."#x002D-#x002E初期化して、WordTokenizerへ移動する
"/"#x002F初期化して、SymbolTokenizer(>)へ移動する
"0-9:"#x0030-#x003A初期化して、WordTokenizerへ移動する
";"#x003B初期化して、JustCreateSymbolTokenizerへ移動する
"<"#x003C初期化して、LeftAngleTokenizerへ移動する
"=>"#x003D-#x003E初期化して、JustCreateSymbolTokenizerへ移動する
"?"#x003F初期化して、SymbolTokenizer2へ移動する
"A-Z"#x0041-#x005A初期化して、WordTokenizerへ移動する
"["#x005B初期化して、JustCreateSymbolTokenizerへ移動する
"]"#x005D初期化して、SymbolTokenizer2へ移動する
"_"#x005F初期化して、WordTokenizerへ移動する
"a-z"#x0061-#x007A初期化して、WordTokenizerへ移動する
"|"#x007C初期化して、JustCreateSymbolTokenizerへ移動する
#x00B7初期化して、WordTokenizerへ移動する
#x00C0-#x00D6初期化して、WordTokenizerへ移動する
#x00D8-#x00F6初期化して、WordTokenizerへ移動する
#x00F8-#x02FF初期化して、WordTokenizerへ移動する
#x0300-#x036F初期化して、WordTokenizerへ移動する
#x0370-#x037D初期化して、WordTokenizerへ移動する
#x037F-#x1FFF初期化して、WordTokenizerへ移動する
#x200C-#x200D初期化して、WordTokenizerへ移動する
#x203F-#x2040初期化して、WordTokenizerへ移動する
#x2070-#x218F初期化して、WordTokenizerへ移動する
#x2C00-#x2FEF初期化して、WordTokenizerへ移動する
#x3001-#xD7FF初期化して、WordTokenizerへ移動する
#xF900-#xFDCF初期化して、WordTokenizerへ移動する
#xFDF0-#xFFFD初期化して、WordTokenizerへ移動する
その他初期化してUnknownTokenを作成する
EOFEofTokenを作成する

NormalSectionLexerの実装


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

    private NormalSectionLexer()
    {
        super();
        
        Init_MovePos wordTokenizer
                     = new Init_MovePos(new WordTokenizer());
        Init_MovePos whiteSpaceTokenizer
                     = new Init_MovePos(new WhiteSpaceTokenizer());
        Init_MovePos doubleQuoteTokenizer
                     = new Init_MovePos(new DoubleQuoteTokenizer());
        Init_MovePos quoteTokenizer
                     = new Init_MovePos(new QuoteTokenizer());

         ArrayList<String> list = new ArrayList<String>();
         list.add("FIXED");
         list.add("IMPLIED");
         list.add("PCDATA");
         list.add("REQUIRED");
         Init_MovePos numberSignTokenizer
                      = new Init_MovePos(new SymbolListTokenizer(list));

         Init_MovePos oneCharSymbol
                      = new Init_MovePos(new JustCreateSymbolTokenizer());

         ArrayList<String> list2 = new ArrayList<String>();
         list2.add("#x");//先に#xを評価させないと#でOKになってしまうので注意
         list2.add("#");
         Init_MovePos ampersandTokenizer
                      = new Init_MovePos(new SymbolListTokenizer2(list2));
         
         ArrayList<String> list3 = new ArrayList<String>();
         list3.add("*");//先に#xを評価させないと#でOKになってしまうので注意
         list3.add("?");
         list3.add("+");
         Init_MovePos rightParenTokenizer
                      = new Init_MovePos(new SymbolListTokenizer2(list3));

         Init_MovePos slashTokenizer
                      = new Init_MovePos(new SymbolTokenizer(">"));
         Init_MovePos leftAngleTokenizer
                      = new Init_MovePos(new LeftAngleTokenizer());
         Init_MovePos questionMarkTokenizer
                      = new Init_MovePos(new SymbolTokenizer2(">"));
         Init_MovePos rightBracketTokenizer
                      = new Init_MovePos(new SymbolTokenizer2("]>"));
         Init_MovePos unknownTokenizer
                      = new Init_MovePos(new UnknownTokenizer());
         EofTokenizer eof                   = new EofTokenizer();

         m_map.put('\u0009'          , whiteSpaceTokenizer);
         m_map.put('\n'              , whiteSpaceTokenizer);//u000A
         m_map.put('\r'              , whiteSpaceTokenizer);//u000D
         m_map.put('\u0020'          , whiteSpaceTokenizer);
         m_map.put('\u0022'          , doubleQuoteTokenizer);
         m_map.put('\u0023'          , numberSignTokenizer);
         m_map.put('\u0025'          , oneCharSymbol);
         m_map.put('\u0026'          , ampersandTokenizer);
         m_map.put('\''              , quoteTokenizer);//u0027
         m_map.put('\u0028'          , oneCharSymbol);
         m_map.put('\u0029'          , rightParenTokenizer);
         m_map.put('\u002A', '\u002C', oneCharSymbol);
         m_map.put('\u002D', '\u002E', wordTokenizer);
         m_map.put('\u002F'          , slashTokenizer);
         m_map.put('\u0030', '\u003A', wordTokenizer);
         m_map.put('\u003B'          , oneCharSymbol);
         m_map.put('\u003C'          , leftAngleTokenizer);
         m_map.put('\u003D', '\u003E', oneCharSymbol);
         m_map.put('\u003F'          , questionMarkTokenizer);
         m_map.put('\u0041', '\u005A', wordTokenizer);
         m_map.put('\u005B'          , oneCharSymbol);
         m_map.put('\u005D'          , rightBracketTokenizer);
         m_map.put('\u005F'          , wordTokenizer);
         m_map.put('\u0061', '\u007A', wordTokenizer);
         m_map.put('\u007C'          , oneCharSymbol);
         m_map.put('\u00B7'          , wordTokenizer);
         m_map.put('\u00C0', '\u00D6', wordTokenizer);
         m_map.put('\u00D8', '\u00F6', wordTokenizer);
         m_map.put('\u00F8', '\u02FF', wordTokenizer);
         m_map.put('\u0300', '\u036F', wordTokenizer);
         m_map.put('\u0370', '\u037D', wordTokenizer);
         m_map.put('\u037F', '\u1FFF', wordTokenizer);
         m_map.put('\u200C', '\u200D', wordTokenizer);
         m_map.put('\u203F', '\u2040', wordTokenizer);
         m_map.put('\u2070', '\u218F', wordTokenizer);
         m_map.put('\u2C00', '\u2FEF', wordTokenizer);
         m_map.put('\u3001', '\uD7FF', wordTokenizer);
         m_map.put('\uF900', '\uFDCF', wordTokenizer);
         m_map.put('\uFDF0', '\uFFFD', wordTokenizer);
         m_map.setOutOfRange(unknownTokenizer);
         m_map.setEofFunctor(eof);
    }

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

NormalSectionLexerで必要なTokenizerの実装

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

WhiteSpaceTokenizer

public class WhiteSpaceTokenizer extends MapSwitchTokenizer
{
    private class WhiteSpaceTokenMaker implements Functor
    {
        public WhiteSpaceTokenMaker(){}
        
        @Override
        public Token tokenize(StringBuilder str, int pos)
        {
            return new WhiteSpaceToken();
        }
    }
    
    public WhiteSpaceTokenizer()
    {
        //
        //  記号       処理
        //  -----------------------------------------------------
        //  \r         今の文字を確定して次へ
        //  \n         今の文字を確定して次へ
        //  \u0009     今の文字を確定して次へ
        //  \u0020     今の文字を確定して次へ
        //  その他     WHITE_SPACEを作成する 
        //  EOF        WHITE_SPACEを作成する 
        //
        //  処理1:今の文字を確定して次へ
        //  処理2:WHITE_SPACEを作成する
        //
        Update_MovePos       proc1 = new Update_MovePos();
        WhiteSpaceTokenMaker proc2 = new WhiteSpaceTokenMaker();
        
        m_map.put('\r'    , proc1);
        m_map.put('\n'    , proc1);
        m_map.put('\u0009', proc1);
        m_map.put('\u0020', proc1);
        m_map.setOutOfRange(proc2);
        m_map.setEofFunctor(proc2);
    }
}

LiteralTokenizer
NormalSectionLexerで出てくるDoubleQuoteTokenizerQuoteTokenizerについて検討します。

どちらのトークナイザも終了文字までを読み込むという意味では同じなので処理もほぼ同じにできます。そこで共通の親となるLiteralTokenizerを作成します。

public abstract class LiteralTokenizer extends MapSwitchTokenizer
{
    private class LiteralTokenMaker implements Functor
    {
        public LiteralTokenMaker(){}
        
        @Override
        public Token tokenize(StringBuilder str, int pos)
        {
            m_tokenBuilder.update(m_char);
            return new LiteralToken();
        }
    }

    public LiteralTokenizer(char endChar)
    {
        //
        //  記号      処理
        //  -----------------------------------------------------
        //  endChar   今の文字を確定して、LITERALを作成する
        //  その他    今の文字を確定して次へ
        //  EOF       UnKnownTokenを作成する
        //
        //  処理1:今の文字を確定して、LITERALを作成する
        //  処理2:今の文字を確定して次へ
        //  処理3:UnKnownTokenを作成する
        //
        
        LiteralTokenMaker proc1 = new LiteralTokenMaker();
        Update_MovePos    proc2 = new Update_MovePos();
        UnknownTokenizer  proc3 = new UnknownTokenizer();
        
        m_map.put(endChar, proc1);
        m_map.setOutOfRange(proc2);
        m_map.setEofFunctor(proc3);
    }
}

DoubleQuoteTokenizer

public class DoubleQuoteTokenizer extends LiteralTokenizer
{
    public DoubleQuoteTokenizer()
    {
        super('"');
    }
}

QuoteTokenizer

public class QuoteTokenizer extends LiteralTokenizer
{
    public QuoteTokenizer()
    {
        super('\'');
    }
}

長くなりましたので、残りの未解説クラスは次回のコラムで説明します。
Comment(0)

コメント

コメントを投稿する