Newer
Older
libj / modules / x-tokenizer / src / tokenizer.cpp
#include <iostream>
#include <sstream>
#include <iomanip>
#include <stdexcept>

#include "string_util.hpp"
#include "tokenizer.hpp"

namespace xtokenizer
{
    const char *createMessage(const char *msg, const ParseInfo &info)
    {
        std::ostringstream ostr;
        ostr << std::endl;
        ostr << info.line << std::endl;

        for (int i = 0; i < info.column; i++)
        {
            ostr << ' ';
        }
        ostr << "^" << std::endl;

        for (int i = 0; i < info.column; i++)
        {
            ostr << ' ';
        }
        ostr << msg << std::endl;

        return ostr.str().c_str();
    }

    const char *createMessage(const char *msg, const Token &token)
    {
        std::ostringstream ostr;
        ostr << std::endl;
        ostr << token.line << std::endl;

        for (int i = 0; i < token.column; i++)
        {
            ostr << ' ';
        }
        ostr << "^" << std::endl;

        for (int i = 0; i < token.column; i++)
        {
            ostr << ' ';
        }
        ostr << msg << std::endl;

        return ostr.str().c_str();
    }

    ParseException::ParseException(const char *msg, const ParseInfo &info)
        : std::logic_error(createMessage(msg, info)) {}

    ParseException::ParseException(const char *msg, const Token &token)
        : std::logic_error(createMessage(msg, token)) {}

    // ParseFunc
    typedef void (*ParseFunc)(ParseInfo &info);

    /**
     * 指定された文字が、シンボルとして利用可能な文字か否か返します。
     *
     * @param c 文字
     * @return シンボルとして利用可能な文字の場合 true
     */
    constexpr bool isSymbolChars(char c)
    {
        return ((('A' <= c) && (c <= 'Z')) || (('a' <= c) && (c <= 'z')) || (('0' <= c) && (c <= '9')) || (c == '_'));
    }

    /**
     * 指定された文字が、2進数として利用可能な文字か否か返します。
     *
     * @param c 文字
     * @return 2進数として利用可能な文字の場合 true
     */
    constexpr bool isBinChars(char c)
    {
        return ((c == '0') || (c == '1') || (c == '_'));
    }

    /**
     * 指定された文字が、8進数として利用可能な文字か否か返します。
     *
     * @param c 文字
     * @return 8進数として利用可能な文字の場合 true
     */
    constexpr bool isOctChars(char c)
    {
        return ((('0' <= c) && (c <= '7')) || (c == '_'));
    }

    /**
     * 指定された文字が、10進数として利用可能な文字か否か返します。
     *
     * @param c 文字
     * @return 10進数として利用可能な文字の場合 true
     */
    constexpr bool isDecChars(char c)
    {
        return ((('0' <= c) && (c <= '9')) || (c == '_'));
    }

    /**
     * 指定された文字が、16進数として利用可能な文字か否か返します。
     *
     * @param c 文字
     * @return 16進数として利用可能な文字の場合 true
     */
    constexpr bool isHexChars(char c)
    {
        return ((('0' <= c) && (c <= '9')) || (('A' <= c) && (c <= 'F')) || (('a' <= c) && (c <= 'f')) || (c == '_'));
    }

    /**
     * 指定された文字を数値に変換します。
     *
     * @param c 文字
     * @return 数値
     */
    constexpr int toNumber(char c)
    {
        return (('0' <= c) && (c <= '9')) ? (c - '0') : (('A' <= c) && (c <= 'F')) ? (c - 'A' + 10)
                                                    : (('a' <= c) && (c <= 'f'))   ? (c - 'a' + 10)
                                                                                   : throw std::invalid_argument("Invalid hexdecimal character");
    }

    /**
     * (共通) 記号解析
     */
    void parseMark(ParseInfo &info, const TokenTypeInfo &tt)
    { // 先頭文字は合致している前提
        const TokenTypeInfo *ttInfo = &tt;
        while (ttInfo != nullptr)
        {
            if (startsWith(info.line, ttInfo->token, info.column))
            { // 合致
                Token token(info.lineNumber, info.column, ttInfo->type, ttInfo->token);
                info.tokens.emplace_back(token);
                info.column += ttInfo->len;
                return;
            }
            // 合致していない
            ttInfo = ttInfo->next;
        }
        throw ParseException("Invalid TokenTypeInfo Chain", info);
    }

    /**
     * (共通) 記号解析
     */
    void parseMark(ParseInfo &info)
    {
        const TokenTypeInfo ttInfo = getTokenTypeInfo(info.line, info.column);
        if (ttInfo.type == TokenType::T_UNKNOWN)
        {
            throw ParseException("[Compiler Bug] Invlid TokenTypeInfo Chain", info);
        }
        Token token(info.lineNumber, info.column, ttInfo.type, ttInfo.token);
        info.tokens.emplace_back(token);
        info.column += ttInfo.len;
    }

    /**
     * (共通) 複数行に跨るブロック解析。
     *
     * @param info 解析情報
     * @param type タイプ
     * @param end  ブロック終了文字列
     * @param endLen ブロック終了文字列長
     * @param isEscape エスケープ有効か否か
     */
    void parseBlock(ParseInfo &info, TokenType type, const char *end, int endLen)
    {
        int endPos = unescapedIndexOf(info.line, end, info.column);
        int startLineNumber = info.lineNumber;
        std::string str = "";
        while (endPos < 0)
        {
            str += escapedString(info.line, info.column, (info.line.length() - info.column));
            str += '\n';
            if (info.readNext())
            {
                endPos = unescapedIndexOf(info.line, end, info.column);
            }
            else
            {
                throw ParseException("not found block end", info);
            }
        }
        // end が見つかった
        int len = endPos - info.column;
        std::string escaped = str + escapedString(info.line, info.column, len);
        Token token(startLineNumber, info.column, type, escaped);
        info.tokens.emplace_back(token);

        // カラム位置を次の位置に設定する
        info.column += len + endLen;
    }

    /**
     * (共通) 複数行に跨るブロック解析(エスケープ文字無効)。
     *
     * @param info 解析情報
     * @param type タイプ
     * @param end  ブロック終了文字列
     * @param endLen ブロック終了文字列長
     * @param isEscape エスケープ有効か否か
     */
    void parseBlockNoEscape(ParseInfo &info, TokenType type, const char *end, int endLen)
    {
        int endPos = indexOf(info.line, end, info.column);
        int startLineNumber = info.lineNumber;
        std::string str = "";
        while (endPos < 0)
        {
            str += info.line.substr(info.column, (info.line.length() - info.column));
            str += '\n';
            if (info.readNext())
            {
                endPos = indexOf(info.line, end, info.column);
            }
            else
            {
                throw ParseException("not found block end", info);
            }
        }
        // end が見つかった
        int len = endPos - info.column;
        std::string escaped = str + info.line.substr(info.column, len);
        Token token(startLineNumber, info.column, type, escaped);
        info.tokens.emplace_back(token);

        // カラム位置を次の位置に設定する
        info.column += len + endLen;
    }

    /**
     * 指定された token の値 (value.i) を設定します。
     *
     * @param info 解析情報(エラー発生時にのみ参照されます)
     * @param token 対象トークン
     * @param base 基数
     * @param offset 文字列解析位置オフセット
     */
    void parseNumberInteger(const ParseInfo &info, Token &token, int base, int offset)
    {
        uint64_t tmp = 0;
        uint64_t val = 0;
        for (std::size_t pos = offset; pos < token.str.length(); pos++)
        {
            if (token.str[pos] != '_')
            {
                tmp = val * base;
                if (tmp < val)
                {
                    throw ParseException("Overflow", info);
                }
                val = tmp;
                val += toNumber(token.str[pos]);
            }
        }
        token.value.i = val;
    }

    /**
     * 指定された token の値 (value.f) を設定します。
     *
     * @param info 解析情報(未使用)
     * @param token 対象トークン
     */
    void parseNumberFloat(const ParseInfo &, Token &token)
    {
        std::size_t pos = 0;

        // 整数部分解析
        double val = 0;
        for (; pos < token.str.length(); pos++)
        {
            if (token.str[pos] == '.')
            {
                pos++;
                break;
            }
            if (token.str[pos] == 'e')
            {
                break;
            }
            if (token.str[pos] != '_')
            {
                val *= 10;
                val += toNumber(token.str[pos]);
            }
        }

        // 小数部分解析
        bool isDiv = false;
        double power = 1;
        for (; pos < token.str.length(); pos++)
        {
            if (token.str[pos] == 'e')
            { // e あり
                pos++;
                if (pos < token.str.length())
                {
                    isDiv = (token.str[pos] == '-');
                    pos++;
                }
                break;
            }
            if (token.str[pos] != '_')
            {
                val *= 10;
                power *= 10;
                val += toNumber(token.str[pos]);
            }
        }

        // 乗数部分解析
        int mul = 0;
        for (; pos < token.str.length(); pos++)
        {
            if (token.str[pos] != '_')
            {
                mul *= 10;
                mul += toNumber(token.str[pos]);
            }
        }

        val = val / power;
        if (isDiv)
        {
            for (int i = 0; i < mul; i++)
            {
                val /= 10;
            }
        }
        else
        {
            for (int i = 0; i < mul; i++)
            {
                val *= 10;
            }
        }
        token.value.f = val;
    }

    /**
     * (共通) 数値解析
     *
     * @param info 解析情報
     * @param isTargetChars 対象文字列判定用関数
     * @param offset オフセット(解析開始位置)
     * @param base 基数
     */
    void parseNumberCore(ParseInfo &info, bool (*isTargetChars)(char), int offset, int base)
    {
        bool (*isChars)(char) = isTargetChars;
        int column = info.column + offset;
        TokenType type = TokenType::T_INTEGER;
        while (column < static_cast<int>(info.line.length()))
        {
            if (!isChars(info.line[column]))
            { // 対象外文字列に到達
                if (isChars != isDecChars)
                { // 10進数でなければ、無条件で終了
                    break;
                }

                // 10進数の場合、小数であれば対応する。
                if ((info.line[column] == '.') && (type == TokenType::T_INTEGER))
                { // ドット or e が存在するため小数
                    type = TokenType::T_FLOAT;
                }
                else if (info.line[column] == 'e')
                { // 乗数部分処理: 乗数部分は10進数
                    type = TokenType::T_FLOAT;
                    if ((column + 2) < static_cast<int>(info.line.length()))
                    {
                        isChars = isDecChars;
                        column += 2;
                    }
                }
                else
                {
                    break;
                }
            }
            column++;
        }
        int len = column - info.column;
        Token token(info.lineNumber, info.column, type, info.line.substr(info.column, len));
        if (type == TokenType::T_FLOAT)
        {
            try
            {
                parseNumberFloat(info, token);
            }
            catch (const std::invalid_argument &e)
            {
                std::ostringstream ostr;
                ostr << "Invalid Float (" << e.what() << ")";
                throw ParseException(ostr.str().c_str(), info);
            }
        }
        else
        {
            try
            {
                parseNumberInteger(info, token, base, offset);
            }
            catch (const std::invalid_argument &e)
            {
                std::ostringstream ostr;
                ostr << "Invalid Integer (" << e.what() << ")";
                ostr << "[token=" << token.str << "]";
                throw ParseException(ostr.str().c_str(), info);
            }
        }
        info.tokens.emplace_back(token);
        info.column += len;
    }

    /**
     * 不正文字。
     *
     * @param info 解析情報
     */
    void parseInvalid(ParseInfo &info)
    {
        throw ParseException("Invlid Charactor", info);
    }

    /**
     * 空白解析。
     *
     * @param info 解析情報
     */
    void parseSpace(ParseInfo &info)
    { // 空白が続く間をトークンとする。
        int startPos = info.column;
        while (std::isspace(info.line[info.column]))
        {
            info.column++;
        }
        Token token(info.lineNumber, startPos, TokenType::T_SPACE, info.line.substr(startPos, (info.column - startPos)));
        info.tokens.emplace_back(token);
    }

    /**
     * 改行解析。
     *
     * @param info 解析情報
     */
    void parseEndOfLine(ParseInfo &info)
    { // getline で読み込んだ場合、ここに来るのは 0x0D (CR) の時のみで
        // 改行処理は別途するため、ここでは何もしない。
        info.column++;
    }

    /**
     * ! 解析。
     *
     * @param info 解析情報
     */
    inline void parseNot(ParseInfo &info)
    {
        parseMark(info, not8);
    }

    /**
     * 文字列リテラル解析。
     *
     * @param info 解析情報
     */
    void parseStringDq(ParseInfo &info)
    {
        if (startsWith(info.line, "\"\"\"", info.column))
        { // 複数行に対応した文字列リテラル
            info.column += 3;
            parseBlock(info, TokenType::T_STRING_T_DQ, "\"\"\"", 3);
        }
        else
        { // 文字列
            info.column += 1;
            parseBlock(info, TokenType::T_STRING_DQ, "\"", 1);
        }
    }

    /**
     * 行コメント (#)
     *
     * @param info 解析情報
     */
    void parseHash(ParseInfo &info)
    {
        Token token(info.lineNumber, info.column, TokenType::T_COMMENT, info.line.substr(info.column + 1));
        info.tokens.emplace_back(token);
        info.column = info.line.length();
    }

    /**
     * $ 解析。
     *
     * @param info 解析情報
     */
    inline void parseDoller(ParseInfo &info)
    {
        parseMark(info, dollar);
    }

    /**
     * % 解析。
     *
     * @param info 解析情報
     */
    inline void parseMod(ParseInfo &info)
    {
        parseMark(info, mod1);
    }

    /**
     * & 解析。
     *
     * @param info 解析情報
     */
    inline void parseAnd(ParseInfo &info)
    {
        parseMark(info, and3);
    }

    /**
     * 文字リテラル解析。
     *
     * @param info 解析情報
     */
    void parseStringSq(ParseInfo &info)
    {
        int endPos = unescapedIndexOf(info.line, "'", info.column + 1);
        if (endPos >= 0)
        {
            std::string escaped = escapedString(info.line, info.column + 1, endPos - info.column - 1);
            Token token(info.lineNumber, info.column, TokenType::T_STRING_SQ, escaped);
            info.tokens.emplace_back(token);
        }
        else
        {
            throw ParseException("not found '\''", info);
        }
    }

    /**
     * ( 解析。
     *
     * @param info 解析情報
     */
    inline void parseLParen(ParseInfo &info)
    {
        parseMark(info, lparen);
    }

    /**
     * ) 解析。
     *
     * @param info 解析情報
     */
    inline void parseRParen(ParseInfo &info)
    {
        parseMark(info, rparen);
    }

    /**
     * * 解析。
     *
     * @param info 解析情報
     */
    inline void parseMul(ParseInfo &info)
    {
        parseMark(info, mul3);
    } // *

    /**
     * + 解析。
     *
     * @param info 解析情報
     */
    inline void parseAdd(ParseInfo &info)
    {
        parseMark(info, add2);
    }

    /**
     * , 解析。
     *
     * @param info 解析情報
     */
    inline void parseComma(ParseInfo &info)
    {
        parseMark(info, comma);
    }

    /**
     * - 解析。
     *
     * @param info 解析情報
     */
    inline void parseSub(ParseInfo &info)
    {
        parseMark(info, sub4);
    }

    /**
     * . 解析。
     *
     * @param info 解析情報
     */
    inline void parseDot(ParseInfo &info)
    {
        parseMark(info, dot2);
    }

    /**
     * / 解析。
     *
     * @param info 解析情報
     */
    void parseDiv(ParseInfo &info)
    {
        switch (info.line[info.column + 1])
        {
        case '/': // 行コメント開始
        {
            Token token(info.lineNumber, info.column, TokenType::T_COMMENT, info.line.substr(info.column + 2));
            info.tokens.emplace_back(token);
            info.column = info.line.length();
            break;
        }
        case '*': // ブロックコメント開始
            info.column += 2;
            parseBlock(info, TokenType::T_COMMENT, "*/", 2);
            break;
        case '+': // ネスト可能なブロックコメント開始
                  // TODO
            break;
        default: // 上記以外
            parseMark(info, div1);
            break;
        }
    }

    /**
     * 0 解析。
     *
     * @param info 解析情報
     */
    void parseNumberZero(ParseInfo &info)
    {
        // 0, 2進数(0b[0-1_]) 8進数(0[0-9]), 16進数(0x[A-Fa-f0-9_]) 判定
        int column = info.column + 1;

        if (column < static_cast<int>(info.line.length()))
        {
            switch (info.line[column])
            {
            case 'b': // 2進数 [0,1,_] で構成
                parseNumberCore(info, isBinChars, 2, 2);
                break;
            case 'x': // 16進数 [0-9,a-f,A-F,_] で構成
                parseNumberCore(info, isHexChars, 2, 16);
                break;
            default:
            {
                if (((column + 1) < static_cast<int>(info.line.length())) && ((info.line[column] == '.') || (info.line[column] == 'e')))
                { // 10進数(小数)
                    parseNumberCore(info, isDecChars, 0, 10);
                }
                else
                { // 8 進数
                    parseNumberCore(info, isOctChars, 1, 8);
                }
                break;
            }
            }
        }
        else
        { // 0
            Token token(info.lineNumber, info.column, TokenType::T_INTEGER, info.line.substr(info.column, 1));
            token.value.i = 0;
            info.tokens.emplace_back(token);
            info.column += 1;
        }
    }

    /**
     * 1-9 解析。
     *
     * @param info 解析情報
     */
    void parseNumber(ParseInfo &info)
    {
        parseNumberCore(info, isDecChars, 0, 10);
    }

    /**
     * : 解析。
     *
     * @param info 解析情報
     */
    inline void parseColon(ParseInfo &info)
    {
        parseMark(info, colon);
    }

    /**
     * ; 解析。
     *
     * @param info 解析情報
     */
    inline void parseSemicolon(ParseInfo &info)
    {
        parseMark(info, semicolon);
    }

    /**
     * < 解析。
     *
     * @param info 解析情報
     */
    inline void parseLt(ParseInfo &info)
    {
        parseMark(info, lt8);
    }

    /**
     * = 解析。
     *
     * @param info 解析情報
     */
    inline void parseEq(ParseInfo &info)
    {
        parseMark(info, eq2);
    }

    /**
     * > 解析。
     *
     * @param info 解析情報
     */
    inline void parseGt(ParseInfo &info)
    {
        parseMark(info, gt5);
    }

    /**
     * ? 解析。
     *
     * @param info 解析情報
     */
    inline void parseQuestion(ParseInfo &info)
    {
        parseMark(info, question3);
    }

    /**
     * @ 解析。
     *
     * @param info 解析情報
     */
    inline void parseAt(ParseInfo &info)
    {
        parseMark(info, at);
    }

    /**
     * A-Z, a-z, _ 解析。
     *
     * @param info 解析情報
     */
    void parseSymbol(ParseInfo &info)
    {
        int column = info.column + 1;
        while (column < static_cast<int>(info.line.length()))
        {
            if (!isSymbolChars(info.line[column]))
            {
                break;
            }
            column++;
        }
        int len = column - info.column;
        Token token(info.lineNumber, info.column, TokenType::T_IDENTIFIER, info.line.substr(info.column, len));
        // キーワード解析
        const KeywordInfo keywordInfo = getKeywordInfo(token.str);
        if (keywordInfo.keyword != Keyword::K_UNKNOWN)
        {
            token.type = TokenType::T_KEYWORD;
            token.keyword = keywordInfo.keyword;
        }
        info.tokens.emplace_back(token);
        info.column += len;
    }

    /**
     * [ 解析。
     *
     * @param info 解析情報
     */
    inline void parseLBracket(ParseInfo &info)
    {
        parseMark(info, lbracket);
    }

    /**
     * '\' 解析。
     *
     * @param info 解析情報
     */
    inline void parseEscape(ParseInfo &info)
    {
        parseMark(info, escape);
    }

    /**
     * ] 解析。
     *
     * @param info 解析情報
     */
    inline void parseRBracket(ParseInfo &info)
    {
        parseMark(info, rbracket);
    }

    /**
     * ^ 解析。
     *
     * @param info 解析情報
     */
    inline void parseHat(ParseInfo &info)
    {
        parseMark(info, hat1);
    }

    /**
     * ` 解析。
     *
     * @param info 解析情報
     */
    inline void parseStringBq(ParseInfo &info)
    {
        if (startsWith(info.line, "```", info.column))
        { // 複数行に対応した文字列リテラル
            info.column += 3;
            parseBlockNoEscape(info, TokenType::T_STRING_T_BQ, "```", 3);
        }
        else
        { // 文字列
            info.column += 1;
            parseBlockNoEscape(info, TokenType::T_STRING_BQ, "`", 1);
        }
    }

    /**
     * { 解析。
     *
     * @param info 解析情報
     */
    inline void parseLCurly(ParseInfo &info)
    {
        parseMark(info, lcurly);
    }

    /**
     * | 解析。
     *
     * @param info 解析情報
     */
    inline void parseOr(ParseInfo &info)
    {
        parseMark(info, or3);
    }

    /**
     * } 解析。
     *
     * @param info 解析情報
     */
    inline void parseRCurly(ParseInfo &info)
    {
        parseMark(info, rcurly);
    }

    /**
     * ~ 解析。
     *
     * @param info 解析情報
     */
    inline void parseTild(ParseInfo &info)
    {
        parseMark(info, tild1);
    }

    constexpr ParseFunc parseTable[] = {
        parseInvalid,    // 0x00:  NUL
        parseInvalid,    // 0x01: SOH (ヘッダ開始)
        parseInvalid,    // 0x02: STX (テキスト開始)
        parseInvalid,    // 0x03: ETX (テキスト終了)
        parseInvalid,    // 0x04: EOT (転送終了)
        parseInvalid,    // 0x05: ENQ (照会)
        parseInvalid,    // 0x06: ACK (受信確認)
        parseInvalid,    // 0x07: BEL (警告)
        parseInvalid,    // 0x08: BS  (後退)
        parseSpace,      // 0x09: HT (水平タブ)
        parseEndOfLine,  // 0x0A: LF (改行)
        parseSpace,      // 0x0B: VT (垂直タブ)
        parseSpace,      // 0x0C: FF (改ページ)
        parseEndOfLine,  // 0x0D: CR (復帰)
        parseInvalid,    // 0x0E: SO (シフトアウト)
        parseInvalid,    // 0x0F: SI (シフトイン)
        parseInvalid,    // 0x10: DLE (データリンクエスケープ)
        parseInvalid,    // 0x11: DC1 (装置制御1)
        parseInvalid,    // 0x12: DC2 (装置制御2)
        parseInvalid,    // 0x13: DC3 (装置制御3)
        parseInvalid,    // 0x14: DC4 (装置制御4)
        parseInvalid,    // 0x15: NAK (受信失敗)
        parseInvalid,    // 0x16: SYN (同期)
        parseInvalid,    // 0x17: ETB (転送ブロック終了)
        parseInvalid,    // 0x18: CAN (キャンセル)
        parseInvalid,    // 0x19: EM  (メディア終了)
        parseInvalid,    // 0x1A: SUB (置換)
        parseInvalid,    // 0x1B: ESC (エスケープ)
        parseInvalid,    // 0x1C: FS  (フォーム区切り)
        parseInvalid,    // 0x1D: GS  (グループ区切り)
        parseInvalid,    // 0x1E: RS  (レコード区切り)
        parseInvalid,    // 0x1F: US  (ユニット区切り)
        parseSpace,      // 0x20: SPA (空白文字)
        parseNot,        // 0x21: !
        parseStringDq,   // 0x22: "
        parseHash,       // 0x23: #
        parseDoller,     // 0x24: $
        parseMod,        // 0x25: %
        parseAnd,        // 0x26: &
        parseStringSq,   // 0x27: '
        parseLParen,     // 0x28: (
        parseRParen,     // 0x29: )
        parseMul,        // 0x2A: *
        parseAdd,        // 0x2B: +
        parseComma,      // 0x2C: ,
        parseSub,        // 0x2D: -
        parseDot,        // 0x2E: .
        parseDiv,        // 0x2F: /
        parseNumberZero, // 0x30: 0
        parseNumber,     // 0x31: 1
        parseNumber,     // 0x32: 2
        parseNumber,     // 0x33: 3
        parseNumber,     // 0x34: 4
        parseNumber,     // 0x35: 5
        parseNumber,     // 0x36: 6
        parseNumber,     // 0x37: 7
        parseNumber,     // 0x38: 8
        parseNumber,     // 0x39: 9
        parseColon,      // 0x3A: :
        parseSemicolon,  // 0x3B: ;
        parseLt,         // 0x3C: <
        parseEq,         // 0x3D: =
        parseGt,         // 0x3E: >
        parseQuestion,   // 0x3F: ?
        parseAt,         // 0x40: @
        parseSymbol,     // 0x41: A
        parseSymbol,     // 0x42: B
        parseSymbol,     // 0x43: C
        parseSymbol,     // 0x44: D
        parseSymbol,     // 0x45: E
        parseSymbol,     // 0x46: F
        parseSymbol,     // 0x47: G
        parseSymbol,     // 0x48: H
        parseSymbol,     // 0x49: I
        parseSymbol,     // 0x4A: J
        parseSymbol,     // 0x4B: K
        parseSymbol,     // 0x4C: L
        parseSymbol,     // 0x4D: M
        parseSymbol,     // 0x4E: N
        parseSymbol,     // 0x4F: O
        parseSymbol,     // 0x50: P
        parseSymbol,     // 0x51: Q
        parseSymbol,     // 0x52: R
        parseSymbol,     // 0x53: S
        parseSymbol,     // 0x54: T
        parseSymbol,     // 0x55: U
        parseSymbol,     // 0x56: V
        parseSymbol,     // 0x57: W
        parseSymbol,     // 0x58: X
        parseSymbol,     // 0x59: Y
        parseSymbol,     // 0x5A: Z
        parseLBracket,   // 0x5B: [
        parseEscape,     // 0x5C: '\'
        parseRBracket,   // 0x5D: ]
        parseHat,        // 0x5E: ^
        parseSymbol,     // 0x5F: _
        parseStringBq,   // 0x60: `
        parseSymbol,     // 0x61: a
        parseSymbol,     // 0x62: b
        parseSymbol,     // 0x63: c
        parseSymbol,     // 0x64: d
        parseSymbol,     // 0x65: e
        parseSymbol,     // 0x66: f
        parseSymbol,     // 0x67: g
        parseSymbol,     // 0x68: h
        parseSymbol,     // 0x69: i
        parseSymbol,     // 0x6A: j
        parseSymbol,     // 0x6B: k
        parseSymbol,     // 0x6C: l
        parseSymbol,     // 0x6D: m
        parseSymbol,     // 0x6E: n
        parseSymbol,     // 0x6F: o
        parseSymbol,     // 0x70: p
        parseSymbol,     // 0x71: q
        parseSymbol,     // 0x72: r
        parseSymbol,     // 0x73: s
        parseSymbol,     // 0x74: t
        parseSymbol,     // 0x75: u
        parseSymbol,     // 0x76: v
        parseSymbol,     // 0x77: w
        parseSymbol,     // 0x78: x
        parseSymbol,     // 0x79: y
        parseSymbol,     // 0x7A: z
        parseLCurly,     // 0x7B: {
        parseOr,         // 0x7C: |
        parseRCurly,     // 0x7D: }
        parseTild,       // 0x7E: }
        parseInvalid,    // 0x7F: DEL(削除)
    };

    ////////////////////////////////////////////////////////////////////////////
    //
    // ParseInfo
    //

    ParseInfo::ParseInfo(const char *file)
        : tokens(), fileName(file), line(), lineNumber(0), column(0), inStream(std::ifstream(file))
    {
        // NOP
    }
    ParseInfo::~ParseInfo()
    {
        inStream.close();
    }

    /**
     * 次の行を読み込みます。
     *
     * @return 読込で来たら true
     */
    bool ParseInfo::readNext()
    {
        if (std::getline(inStream, line))
        {
            lineNumber++;
            column = 0;
            std::cout << line << std::endl;
            return true;
        }
        return false;
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Tokenizer
    //

    Tokenizer::Tokenizer(const char *file)
        : parseInfo(file)
    {
        // NOP
    }

    Tokenizer::~Tokenizer()
    {
        // NOP
    }

    std::vector<Token> &Tokenizer::parse()
    {
        if (!parseInfo.inStream.is_open())
        { // error
            return parseInfo.tokens;
        }

        parseInfo.lineNumber = 0;
        while (parseInfo.readNext())
        {
            while (static_cast<size_t>(parseInfo.column) < parseInfo.line.length())
            {
                unsigned char val = static_cast<unsigned char>(parseInfo.line[parseInfo.column]);
                if ((val & 0x80) == 0)
                {
                    parseTable[val](parseInfo);
                }
                else
                {
                    std::cout << "Invalid value" << std::endl;
                }
            }
        }
        return parseInfo.tokens;
    }

} // namespace xtokenizer