Пример #1
0
        /// <summary>
        /// 現在の位置から範囲コメントのトークンを 1 つ取得します。
        /// </summary>
        /// <returns>取得した範囲コメントのトークン。</returns>
        /// <remarks>
        /// このメソッドは取得したトークンの文字数だけ位置を進めます。
        /// また、このメソッドを呼び出す前に位置を "/*" の前に設定することに注意して下さい。
        /// </remarks>
        private NakoToken GetRangeCommentToken()
        {
            var token = new NakoToken(NakoTokenType.COMMENT, lineNo, indentLevel);

            // "/*" をスキップ
            position += 2;
            // コメントの最後までを取得
            string comment = "";

            while (!IsEOF)
            {
                char ch = CurrentChar;
                if (ch == '\n')
                {
                    lineNo++; // 行番号がずれてしまうので重要
                }
                if (ch == '*' && NextChar == '/')
                {
                    position += 2;
                    break;
                }
                comment += ch;
                position++;
            }
            // コメントの文字列を設定
            token.Value = comment;
            return(token);
        }
Пример #2
0
        /// <summary>
        /// 現在の位置から数字のトークンを 1 つ取得します。
        /// </summary>
        /// <returns>取得した数字のトークン。</returns>
        /// <remarks>
        /// このメソッドは取得したトークンの文字数だけ位置を進めます。
        /// </remarks>
        private NakoToken GetNumberToken()
        {
            var    token = new NakoToken(NakoTokenType.INT, lineNo, indentLevel);
            string str   = "";

            for (; !IsEOF; position++)
            {
                char ch = CurrentChar;
                if (!NakoUtility.IsNumber(ch))
                {
                    break;
                }
                str += ch;
            }
            if (CurrentChar == '.' && NakoUtility.IsNumber(NextChar))
            {
                str       += CurrentChar;
                token.Type = NakoTokenType.NUMBER;
                position++;
                for (; !IsEOF; position++)
                {
                    char ch = CurrentChar;
                    if (!NakoUtility.IsNumber(ch))
                    {
                        break;
                    }
                    str += ch;
                }
            }
            token.Value = str;
            CheckJosi(token);
            return(token);
        }
Пример #3
0
        /// <summary>
        /// INCLUDEのチェックとINCLUDEの実行。
        /// </summary>
        private void Include()
        {
            // INCLUDEのチェック
            tokens.MoveTop();
            NakoToken previousToken = tokens.CurrentToken;

            for (; !tokens.IsEOF(); tokens.MoveNext())
            {
                // TOKENを挿入
                if (tokens.CurrentTokenType == NakoTokenType.INCLUDE)
                {
                    string        filename       = previousToken.Value;
                    string        source         = File.ReadAllText(filename);
                    NakoToken     currentToken   = tokens.CurrentToken;
                    int           index          = tokens.IndexOf(currentToken);
                    NakoTokenList includedTokens = new NakoTokenizer().Tokenize(source, this.tokenDic);
                    int           insertIndex    = index;
                    foreach (NakoToken tok in includedTokens)
                    {
                        insertIndex++;
                        tokens.Insert(insertIndex, tok);
                    }
                    tokens.Remove(previousToken);
                    tokens.Remove(currentToken);
                }
                previousToken = tokens.CurrentToken;
            }
        }
Пример #4
0
 /// <summary>
 /// 関数宣言を辞書に登録します。
 /// </summary>
 private void DefineFunction()
 {
     tokens.MoveTop();
     for (; !tokens.IsEOF(); tokens.MoveNext())
     {
         if (tokens.CurrentTokenType == NakoTokenType.DEF_FUNCTION)
         {
             var firstToken = tokens.CurrentToken;
             // DEF_FUNCTION をスキップ
             tokens.MoveNext();
             // 関数名のトークン
             NakoToken funcNameToken = null;
             for (; !tokens.IsEOF(); tokens.MoveNext())
             {
                 // 関数名
                 if (tokens.Accept(NakoTokenType.WORD))
                 {
                     funcNameToken = tokens.CurrentToken;
                 }
                 // 改行ならば関数宣言の終了
                 else if (tokens.Accept(NakoTokenType.SCOPE_BEGIN) || tokens.Accept(NakoTokenType.PARENTHESES_L))
                 {
                     if (funcNameToken == null)
                     {
                         throw new NakoTokenizerException("関数宣言で関数名がありません。", firstToken);
                     }
                     // 関数名を辞書に登録
                     tokenDic[funcNameToken.GetValueAsName()] = NakoTokenType.FUNCTION_NAME;
                     funcNameToken.Type = NakoTokenType.FUNCTION_NAME;
                     break;
                 }
             }
         }
     }
 }
Пример #5
0
        /// <summary>
        /// 現在の位置から一行コメントのトークンを 1 つ取得します。
        /// </summary>
        /// <returns>取得した一行コメントのトークン。</returns>
        /// <remarks>
        /// このメソッドは取得したトークンの文字数だけ位置を進めます。
        /// また、このメソッドを呼び出す前に位置を "//"、または "#" の前に設定することに注意して下さい。
        /// </remarks>
        private NakoToken GetLineCommentToken()
        {
            var token = new NakoToken(NakoTokenType.COMMENT, lineNo, indentLevel);

            if (CurrentChar == '/')
            {
                // "//" をスキップ
                position += 2;
            }
            else
            {
                // "#" をスキップ
                position++;
            }
            // 行末までスキップ
            string comment = "";

            while (!IsEOF)
            {
                char ch = CurrentCharRaw;
                if (ch == '\r' || ch == '\n')
                {
                    break;
                }
                comment += ch;
                position++;
            }
            // コメントの文字列を設定
            token.Value = comment;
            return(token);
        }
Пример #6
0
        /// <summary>
        /// 現在の位置から単語のトークンを 1 つ取得します。
        /// </summary>
        /// <returns>取得した単語のトークン。</returns>
        /// <remarks>
        /// このメソッドは取得したトークンの文字数だけ位置を進めます。
        /// </remarks>
        private NakoToken GetWordToken()
        {
            var token   = new NakoToken(NakoTokenType.WORD, lineNo, indentLevel);
            var builder = new StringBuilder();

            while (!IsEOF)
            {
                char c = CurrentChar;
                if (NakoUtility.IsAlpha(c) || NakoUtility.IsNumber(c) || c == '_' || c == '!' || c == '?')
                {
                    builder.Append(c);
                    position++;
                    continue;
                }
                // 助詞なら区切る
                if (NakoUtility.IsHiragana(c))
                {
                    if (CheckJosi(token))
                    {
                        break;
                    }
                }
                // 全角文字なら読む
                if (c >= 0xFF)
                {
                    builder.Append(c);
                    position++;
                    // 特別な予約語なら区切る
                    if (builder.ToString() == "もし" || builder.ToString() == "ならば")
                    {
                        break;
                    }
                    continue;
                }
                break;
            }
            token.Value = builder.ToString();
            return(token);
        }
Пример #7
0
 /// <summary>
 /// 現在の位置から助詞が存在するかどうかを調査します。もし助詞が存在する場合は、指定したトークンに助詞を設定します。
 /// </summary>
 /// <param name="token">助詞を設定するトークン。</param>
 /// <returns>助詞が存在する場合は true。それ以外の場合は false。</returns>
 /// <remarks>
 /// このメソッドは、助詞が存在する場合、助詞の文字数だけ位置を進めます。
 /// </remarks>
 private bool CheckJosi(NakoToken token)
 {
     if (IsEOF)
     {
         return(false);
     }
     // 助詞はひらがななので
     if (!NakoUtility.IsHiragana(CurrentChar))
     {
         return(false);
     }
     // 助詞を 1 つずつ調べる
     foreach (string josi in NakoJosi.Instance)
     {
         if (this.Equals(josi))
         {
             token.Josi = josi;
             position  += josi.Length;
             return(true);
         }
     }
     return(false);
 }
Пример #8
0
        /// <summary>
        /// 現在の位置のスコープを確認します。もし、スコープに変更がある場合は、インデントレベルを変更し、スコープのトークンを追加します。
        /// </summary>
        private void CheckScope()
        {
            int newIndentCount = CountIndent();

            if (newIndentCount == indentCount)
            {
                return;
            }
            if (newIndentCount > indentCount)
            {
                indentLevel++;
                tokens.Add(new NakoToken(NakoTokenType.SCOPE_BEGIN, lineNo, indentLevel));
                indentStack.Push(newIndentCount);
            }
            else
            {
                var errorToken = new NakoToken(NakoTokenType.SCOPE_END, lineNo, indentLevel);
                // 連続で POP する可能性がある
                while (true)
                {
                    int check = indentStack.Peek();
                    if (check == newIndentCount)
                    {
                        break;
                    }
                    if (check < newIndentCount)
                    {
                        throw new NakoTokenizerException("インデントレベルが間違っています。", errorToken);
                    }
                    indentLevel--;
                    indentStack.Pop();
                    tokens.Add(new NakoToken(NakoTokenType.SCOPE_END, lineNo, indentLevel));
                }
            }
            indentCount = newIndentCount;
        }
Пример #9
0
 /// <summary>
 /// 指定したエラー メッセージ、この例外の原因であるトークン、この例外の原因である内部例外への参照を使用して、NakoTokenizerException クラスの新しいインスタンスを初期化します。
 /// </summary>
 /// <param name="message">例外の原因を説明するエラー メッセージ。</param>
 /// <param name="token">例外の原因であるトークン。</param>
 /// <param name="innerException">現在の例外の原因である例外。innerException パラメータが null 参照でない場合は、内部例外を処理する catch ブロックで現在の例外が発生します。</param>
 public NakoTokenizerException(string message, NakoToken token, Exception innerException)
     : base(message, innerException)
 {
     Token = token;
 }
Пример #10
0
 /// <summary>
 /// 展開あり文字列トークンを再帰的に展開します。
 /// </summary>
 /// <param name="token">展開あり文字列トークン。</param>
 /// <returns>再帰的に展開したトークン一覧。</returns>
 private static NakoTokenList StringTokenExtract(NakoToken token)
 {
     var tokens = new NakoTokenList();
     string tmp = "";
     string str = token.Value;
     int i = 0;
     bool isFirst = true;
     while (i < str.Length)
     {
         char ch = str[i];
         if (ch == '{' || ch == '{')
         {
             if (isFirst)
             {
                 isFirst = false;
             }
             else
             {
                 // "&" トークンを追加
                 tokens.Add(new NakoToken(NakoTokenType.AND, token.LineNo, token.IndentLevel));
             }
             i++;
             // 展開する文字列 ("{" と "}" との間) を取得する
             string exString = "";
             {
                 char end = (ch == '{') ? '}' : '}';
                 for (; i < str.Length; i++)
                 {
                     if (str[i] == end)
                     {
                         i++;
                         break;
                     }
                     exString += str[i];
                 }
             }
             // 文字列展開だけの特殊メソッド ('\' メソッド)
             if (exString.Length > 0 && exString[0] == '\\')
             {
                 if (exString == "\\t")
                 {
                     // \t の場合
                     tmp += '\t';
                     exString = "";
                 }
                 else if (exString == "\\r")
                 {
                     // \r の場合
                     tmp += '\r';
                     exString = "";
                 }
                 else if (exString == "\\n")
                 {
                     // \n の場合
                     tmp += '\n';
                     exString = "";
                 }
                 else if (NakoUtility.IsNumber(exString[1]))
                 {
                     // \0 のような場合
                     exString = exString.Substring(1);
                     tmp += (char)int.Parse(exString);
                     exString = "";
                 }
                 else if (exString[1] == '$')
                 {
                     // \$00 のような場合
                     exString = "0x" + exString.Substring(2);
                     tmp += (char)int.Parse(exString);
                     exString = "";
                 }
                 else
                 {
                     new NakoTokenizerException("展開あり文字列内の利用できない`\\'メソッド:" + exString, token);
                 }
             }
             // 文字列展開だけの特殊メソッド ('~' メソッド)
             else if (exString.Length == 1 && exString[0] == '~')
             {
                 tmp += "\r\n";
                 exString = "";
             }
             // 文字列トークンを追加
             tokens.Add(new NakoToken(NakoTokenType.STRING, token.LineNo, token.IndentLevel, tmp));
             tmp = "";
             if (exString != "")
             {
                 // "&" トークンを追加
                 tokens.Add(new NakoToken(NakoTokenType.AND, token.LineNo, token.IndentLevel));
                 // "(" トークンを追加
                 tokens.Add(new NakoToken(NakoTokenType.PARENTHESES_L, token.LineNo, token.IndentLevel));
                 // 再帰的にトークンを解析
                 var innerTokens = new NakoTokenizer().TokenizeSplitOnly(exString, token.LineNo, token.IndentLevel); // とりあえず区切るだけ
                 foreach (var innerToken in innerTokens)
                 {
                     tokens.Add(innerToken);
                 }
                 // ")" トークンを追加
                 tokens.Add(new NakoToken(NakoTokenType.PARENTHESES_R, token.LineNo, token.IndentLevel));
             }
             continue;
         }
         tmp += ch;
         i++;
     }
     if (tmp != "")
     {
         if (!isFirst)
         {
             // "&" トークンを追加
             tokens.Add(new NakoToken(NakoTokenType.AND, token.LineNo, token.IndentLevel));
         }
         // 文字列トークンを追加
         tokens.Add(new NakoToken(NakoTokenType.STRING, token.LineNo, token.IndentLevel, tmp));
     }
     else
     {
         // 必要なら空文字列トークンを追加
         if (isFirst)
         {
             tokens.Add(new NakoToken(NakoTokenType.STRING, token.LineNo, token.IndentLevel, ""));
         }
     }
     // 助詞をコピー
     tokens[tokens.Count - 1].Josi = token.Josi;
     return tokens;
 }
Пример #11
0
 /// <summary>
 /// 現在の位置のスコープを確認します。もし、スコープに変更がある場合は、インデントレベルを変更し、スコープのトークンを追加します。
 /// </summary>
 private void CheckScope()
 {
     int newIndentCount = CountIndent();
     if (newIndentCount == indentCount)
     {
         return;
     }
     if (newIndentCount > indentCount)
     {
         indentLevel++;
         tokens.Add(new NakoToken(NakoTokenType.SCOPE_BEGIN, lineNo, indentLevel));
         indentStack.Push(newIndentCount);
     }
     else
     {
         var errorToken = new NakoToken(NakoTokenType.SCOPE_END, lineNo, indentLevel);
         // 連続で POP する可能性がある
         while (true)
         {
             int check = indentStack.Peek();
             if (check == newIndentCount)
             {
                 break;
             }
             if (check < newIndentCount)
             {
                 throw new NakoTokenizerException("インデントレベルが間違っています。", errorToken);
             }
             indentLevel--;
             indentStack.Pop();
             tokens.Add(new NakoToken(NakoTokenType.SCOPE_END, lineNo, indentLevel));
         }
     }
     indentCount = newIndentCount;
 }
Пример #12
0
 /// <summary>
 /// 現在の位置から単語のトークンを 1 つ取得します。
 /// </summary>
 /// <returns>取得した単語のトークン。</returns>
 /// <remarks>
 /// このメソッドは取得したトークンの文字数だけ位置を進めます。
 /// </remarks>
 private NakoToken GetWordToken()
 {
     var token = new NakoToken(NakoTokenType.WORD, lineNo, indentLevel);
     var builder = new StringBuilder();
     while (!IsEOF)
     {
         char c = CurrentChar;
         if (NakoUtility.IsAlpha(c) || NakoUtility.IsNumber(c) || c == '_' || c == '!' || c == '?')
         {
             builder.Append(c);
             position++;
             continue;
         }
         // 助詞なら区切る
         if (NakoUtility.IsHiragana(c))
         {
             if (CheckJosi(token)) break;
         }
         // 全角文字なら読む
         if (c >= 0xFF)
         {
             builder.Append(c);
             position++;
             // 特別な予約語なら区切る
             if (builder.ToString() == "もし" || builder.ToString() == "ならば")
             {
                 break;
             }
             continue;
         }
         break;
     }
     token.Value = builder.ToString();
     return token;
 }
Пример #13
0
 /// <summary>
 /// 現在の位置からトークンを 1 つ取得します。
 /// </summary>
 /// <returns>取得したトークン。位置がソースコードの終端に達している場合は null。</returns>
 /// <remarks>
 /// このメソッドは取得したトークンの文字数だけ位置を進めます。
 /// </remarks>
 private NakoToken GetToken()
 {
     if (IsEOF)
     {
         return null;
     }
     var token = new NakoToken(NakoTokenType.UNKNOWN, lineNo, indentLevel);
     char nc;
     switch (CurrentChar)
     {
         // BOM かどうか確認
         case (char)0xFEFF:
             position++;
             return null;
         // 行末かどうか確認
         case '\r':
             position++;
             return null;
         case '\n':
             token.Type = NakoTokenType.EOL;
             position++;
             lineNo++;
             tokens.Add(token);
             CheckScope();
             return null;
         // インデントかどうか確認
         case ' ':
         case '\t':
             position++; // skip
             return null;
         // 句読点かどうか確認
         case ';':
             token.Type = NakoTokenType.EOL; // 明確な区切り
             position++;
             return token;
         case ',':
             position++;
             return null;
         // 記号かどうか確認
         case '=':
             nc = NextChar;
             if (nc == '=')
             {
                 position += 2;
                 token.Type = NakoTokenType.EQ_EQ;
             }
             else
             {
                 position++;
                 token.Type = NakoTokenType.EQ;
             }
             return token;
         case '&':
             nc = NextChar;
             if (nc == '&')
             {
                 position += 2;
                 token.Type = NakoTokenType.AND_AND;
             }
             else
             {
                 position++;
                 token.Type = NakoTokenType.AND;
             }
             return token;
         case '|':
             nc = NextChar;
             if (nc == '|')
             {
                 position += 2;
                 token.Type = NakoTokenType.OR_OR;
             }
             else
             {
                 position++;
                 token.Type = NakoTokenType.OR;
             }
             return token;
         case '<':
             nc = NextChar;
             if (nc == '=')
             {
                 position += 2;
                 token.Type = NakoTokenType.LT_EQ;
             }
             else if (nc == '>')
             {
                 position += 2;
                 token.Type = NakoTokenType.NOT_EQ;
             }
             else
             {
                 position++;
                 token.Type = NakoTokenType.LT;
             }
             return token;
         case '>':
             nc = NextChar;
             if (nc == '=')
             {
                 position += 2;
                 token.Type = NakoTokenType.GT_EQ;
             }
             else if (nc == '<')
             {
                 position += 2;
                 token.Type = NakoTokenType.NOT_EQ;
             }
             else
             {
                 position++;
                 token.Type = NakoTokenType.GT;
             }
             return token;
         case '!':
             nc = NextChar;
             if (nc == '=')
             {
                 position += 2;
                 token.Type = NakoTokenType.NOT_EQ;
             }
             else
             {
                 position++;
                 token.Type = NakoTokenType.NOT;
             }
             return token;
         case '「':
         case '『':
         case '"':
         case '`':
             return GetStringToken();
         case '+':
             token.Type = NakoTokenType.PLUS;
             position++;
             return token;
         case '-':
             token.Type = NakoTokenType.MINUS;
             position++;
             return token;
         case '*':
             if (lastTokenType == NakoTokenType.EOL ||
                 lastTokenType == NakoTokenType.UNKNOWN)
             {
                 token.Type = NakoTokenType.DEF_FUNCTION;
             }
             else
             {
                 token.Type = NakoTokenType.MUL;
             }
             position++;
             return token;
         case '/':
             // コメントかどうか確認
             nc = NextChar;
             if (nc == '*') return GetRangeCommentToken();
             if (nc == '/') return GetLineCommentToken();
             // 割り算かどうか確認
             token.Type = NakoTokenType.DIV;
             position++;
             return token;
         case '%':
             token.Type = NakoTokenType.MOD;
             position++;
             return token;
         case '^':
             token.Type = NakoTokenType.POWER;
             position++;
             return token;
         case '(':
             token.Type = NakoTokenType.PARENTHESES_L;
             position++;
             return token;
         case ')':
             token.Type = NakoTokenType.PARENTHESES_R;
             position++;
             CheckJosi(token);
             return token;
         case '{':
             token.Type = NakoTokenType.BRACES_L;
             position++;
             return token;
         case '}':
             token.Type = NakoTokenType.BRACES_R;
             position++;
             CheckJosi(token);
             return token;
         case '[':
             token.Type = NakoTokenType.BRACKETS_L;
             position++;
             return token;
         case ']':
             token.Type = NakoTokenType.BRACKETS_R;
             position++;
             CheckJosi(token);
             return token;
         case '\\':
             token.Type = NakoTokenType.YEN;
             position++;
             return token;
         case '#':
             return GetLineCommentToken();
         default:
             token = GetNotFlagToken();
             if (token.Type == NakoTokenType.UNKNOWN)
             {
                 char ch = CurrentChar;
                 string msg = "未定義の文字列: " + (ch < 0x20 ? String.Format("0x{0,0:X2}", (int)ch) : "`" + ch + "`");
                 throw new NakoTokenizerException(msg, token);
             }
             return token;
     }
 }
Пример #14
0
 /// <summary>
 /// 現在の位置から文字列のトークンを 1 つ取得します。
 /// </summary>
 /// <returns>取得した文字列のトークン。</returns>
 /// <remarks>
 /// このメソッドは取得したトークンの文字数だけ位置を進めます。
 /// また、このメソッドを呼び出す前に位置を '「'、'『'、'"'、または '`' の前に設定することに注意して下さい。
 /// </remarks>
 private NakoToken GetStringToken()
 {
     if (IsEOF) return null;
     var token = new NakoToken(NakoTokenType.STRING, lineNo, indentLevel);
     char start = CurrentChar;
     string stringEnd;
     // 終端文字列の判別
     // S = 「...」
     switch (start)
     {
         case '「': stringEnd = "」"; token.Type = NakoTokenType.STRING_EX; break;
         case '『': stringEnd = "』"; token.Type = NakoTokenType.STRING; break;
         case '"': stringEnd = "\""; token.Type = NakoTokenType.STRING_EX; break;
         case '`': stringEnd = "`"; token.Type = NakoTokenType.STRING; break;
         default: throw new NakoTokenizerException(/*TODO*/);
     }
     position++;
     // ヒアドキュメント文字列
     // S = 「「 ... 」」
     // S = 「「「 ... 」」」
     if (start == '「' || start == '『')
     {
         position--;
         stringEnd = "";
         while (!IsEOF)
         {
             if (CurrentChar == '「')
             {
                 token.Type = NakoTokenType.STRING_EX;
                 stringEnd += '」';
                 position++;
             }
             else if (CurrentChar == '『')
             {
                 token.Type = NakoTokenType.STRING;
                 stringEnd += '』';
                 position++;
             }
             else break;
         }
     }
     // 文字列の終端まで取得
     var builder = new StringBuilder();
     bool isSkipBlank = false; // 空白のスキップを行うかどうか
     while (!IsEOF)
     {
         if (Equals(stringEnd))
         {
             position += stringEnd.Length;
             break;
         }
         char c = CurrentCharRaw;
         position++;
         if (isSkipBlank)
         {
             if (c == ' ' || c == ' ' || c == '\t')
             {
                 continue;
             }
             isSkipBlank = false;
         }
         if (c == '\n')
         {
             lineNo++;
             isSkipBlank = true;
         }
         builder.Append(c);
     }
     token.Value = builder.ToString();
     CheckJosi(token);
     return token;
 }
Пример #15
0
 /// <summary>
 /// 現在の位置から範囲コメントのトークンを 1 つ取得します。
 /// </summary>
 /// <returns>取得した範囲コメントのトークン。</returns>
 /// <remarks>
 /// このメソッドは取得したトークンの文字数だけ位置を進めます。
 /// また、このメソッドを呼び出す前に位置を "/*" の前に設定することに注意して下さい。
 /// </remarks>
 private NakoToken GetRangeCommentToken()
 {
     var token = new NakoToken(NakoTokenType.COMMENT, lineNo, indentLevel);
     // "/*" をスキップ
     position += 2;
     // コメントの最後までを取得
     string comment = "";
     while (!IsEOF)
     {
         char ch = CurrentChar;
         if (ch == '\n')
         {
             lineNo++; // 行番号がずれてしまうので重要
         }
         if (ch == '*' && NextChar == '/')
         {
             position += 2;
             break;
         }
         comment += ch;
         position++;
     }
     // コメントの文字列を設定
     token.Value = comment;
     return token;
 }
Пример #16
0
        /// <summary>
        /// 現在の位置から数字のトークンを 1 つ取得します。
        /// </summary>
        /// <returns>取得した数字のトークン。</returns>
        /// <remarks>
        /// このメソッドは取得したトークンの文字数だけ位置を進めます。
        /// </remarks>
        private NakoToken GetNumberToken()
        {
            var token = new NakoToken(NakoTokenType.INT, lineNo, indentLevel);
            string str = "";
            for (; !IsEOF; position++)
            {
                char ch = CurrentChar;
                if (!NakoUtility.IsNumber(ch)) break;
                str += ch;
            }
            if (CurrentChar == '.' && NakoUtility.IsNumber(NextChar))
            {
                str += CurrentChar;
                token.Type = NakoTokenType.NUMBER;
                position++;
                for (; !IsEOF; position++)
                {
                    char ch = CurrentChar;
                    if (!NakoUtility.IsNumber(ch)) break;
                    str += ch;

                }
            }
            token.Value = str;
            CheckJosi(token);
            return token;
        }
Пример #17
0
 /// <summary>
 /// 現在の位置から一行コメントのトークンを 1 つ取得します。
 /// </summary>
 /// <returns>取得した一行コメントのトークン。</returns>
 /// <remarks>
 /// このメソッドは取得したトークンの文字数だけ位置を進めます。
 /// また、このメソッドを呼び出す前に位置を "//"、または "#" の前に設定することに注意して下さい。
 /// </remarks>
 private NakoToken GetLineCommentToken()
 {
     var token = new NakoToken(NakoTokenType.COMMENT, lineNo, indentLevel);
     if (CurrentChar == '/')
     {
         // "//" をスキップ
         position += 2;
     }
     else
     {
         // "#" をスキップ
         position++;
     }
     // 行末までスキップ
     string comment = "";
     while (!IsEOF)
     {
         char ch = CurrentCharRaw;
         if (ch == '\r' || ch == '\n') break;
         comment += ch;
         position++;
     }
     // コメントの文字列を設定
     token.Value = comment;
     return token;
 }
Пример #18
0
        /// <summary>
        /// 現在の位置からトークンを 1 つ取得します。
        /// </summary>
        /// <returns>取得したトークン。位置がソースコードの終端に達している場合は null。</returns>
        /// <remarks>
        /// このメソッドは取得したトークンの文字数だけ位置を進めます。
        /// </remarks>
        private NakoToken GetToken()
        {
            if (IsEOF)
            {
                return(null);
            }
            var  token = new NakoToken(NakoTokenType.UNKNOWN, lineNo, indentLevel);
            char nc;

            switch (CurrentChar)
            {
            // BOM かどうか確認
            case (char)0xFEFF:
                position++;
                return(null);

            // 行末かどうか確認
            case '\r':
                position++;
                return(null);

            case '\n':
                token.Type = NakoTokenType.EOL;
                position++;
                lineNo++;
                tokens.Add(token);
                CheckScope();
                return(null);

            // インデントかどうか確認
            case ' ':
            case '\t':
                position++;     // skip
                return(null);

            // 句読点かどうか確認
            case ';':
                token.Type = NakoTokenType.EOL;     // 明確な区切り
                position++;
                return(token);

            case ',':
                position++;
                return(null);

            // 記号かどうか確認
            case '=':
                nc = NextChar;
                if (nc == '=')
                {
                    position  += 2;
                    token.Type = NakoTokenType.EQ_EQ;
                }
                else
                {
                    position++;
                    token.Type = NakoTokenType.EQ;
                }
                return(token);

            case '&':
                nc = NextChar;
                if (nc == '&')
                {
                    position  += 2;
                    token.Type = NakoTokenType.AND_AND;
                }
                else
                {
                    position++;
                    token.Type = NakoTokenType.AND;
                }
                return(token);

            case '|':
                nc = NextChar;
                if (nc == '|')
                {
                    position  += 2;
                    token.Type = NakoTokenType.OR_OR;
                }
                else
                {
                    position++;
                    token.Type = NakoTokenType.OR;
                }
                return(token);

            case '<':
                nc = NextChar;
                if (nc == '=')
                {
                    position  += 2;
                    token.Type = NakoTokenType.LT_EQ;
                }
                else if (nc == '>')
                {
                    position  += 2;
                    token.Type = NakoTokenType.NOT_EQ;
                }
                else
                {
                    position++;
                    token.Type = NakoTokenType.LT;
                }
                return(token);

            case '>':
                nc = NextChar;
                if (nc == '=')
                {
                    position  += 2;
                    token.Type = NakoTokenType.GT_EQ;
                }
                else if (nc == '<')
                {
                    position  += 2;
                    token.Type = NakoTokenType.NOT_EQ;
                }
                else
                {
                    position++;
                    token.Type = NakoTokenType.GT;
                }
                return(token);

            case '!':
                nc = NextChar;
                if (nc == '=')
                {
                    position  += 2;
                    token.Type = NakoTokenType.NOT_EQ;
                }
                else
                {
                    position++;
                    token.Type = NakoTokenType.NOT;
                }
                return(token);

            case '「':
            case '『':
            case '"':
            case '`':
                return(GetStringToken());

            case '+':
                token.Type = NakoTokenType.PLUS;
                position++;
                return(token);

            case '-':
                token.Type = NakoTokenType.MINUS;
                position++;
                return(token);

            case '*':
                if (lastTokenType == NakoTokenType.EOL ||
                    lastTokenType == NakoTokenType.UNKNOWN)
                {
                    token.Type = NakoTokenType.DEF_FUNCTION;
                }
                else
                {
                    token.Type = NakoTokenType.MUL;
                }
                position++;
                return(token);

            case '/':
                // コメントかどうか確認
                nc = NextChar;
                if (nc == '*')
                {
                    return(GetRangeCommentToken());
                }
                if (nc == '/')
                {
                    return(GetLineCommentToken());
                }
                // 割り算かどうか確認
                token.Type = NakoTokenType.DIV;
                position++;
                return(token);

            case '%':
                token.Type = NakoTokenType.MOD;
                position++;
                return(token);

            case '^':
                token.Type = NakoTokenType.POWER;
                position++;
                return(token);

            case '(':
                token.Type = NakoTokenType.PARENTHESES_L;
                position++;
                return(token);

            case ')':
                token.Type = NakoTokenType.PARENTHESES_R;
                position++;
                CheckJosi(token);
                return(token);

            case '{':
                token.Type = NakoTokenType.BRACES_L;
                position++;
                return(token);

            case '}':
                token.Type = NakoTokenType.BRACES_R;
                position++;
                CheckJosi(token);
                return(token);

            case '[':
                token.Type = NakoTokenType.BRACKETS_L;
                position++;
                return(token);

            case ']':
                token.Type = NakoTokenType.BRACKETS_R;
                position++;
                CheckJosi(token);
                return(token);

            case '\\':
                token.Type = NakoTokenType.YEN;
                position++;
                return(token);

            case '#':
                return(GetLineCommentToken());

            default:
                token = GetNotFlagToken();
                if (token.Type == NakoTokenType.UNKNOWN)
                {
                    char   ch  = CurrentChar;
                    string msg = "未定義の文字列: " + (ch < 0x20 ? String.Format("0x{0,0:X2}", (int)ch) : "`" + ch + "`");
                    throw new NakoTokenizerException(msg, token);
                }
                return(token);
            }
        }
Пример #19
0
        /// <summary>
        /// 現在の位置から文字列のトークンを 1 つ取得します。
        /// </summary>
        /// <returns>取得した文字列のトークン。</returns>
        /// <remarks>
        /// このメソッドは取得したトークンの文字数だけ位置を進めます。
        /// また、このメソッドを呼び出す前に位置を '「'、'『'、'"'、または '`' の前に設定することに注意して下さい。
        /// </remarks>
        private NakoToken GetStringToken()
        {
            if (IsEOF)
            {
                return(null);
            }
            var    token = new NakoToken(NakoTokenType.STRING, lineNo, indentLevel);
            char   start = CurrentChar;
            string stringEnd;

            // 終端文字列の判別
            // S = 「...」
            switch (start)
            {
            case '「': stringEnd = "」"; token.Type = NakoTokenType.STRING_EX; break;

            case '『': stringEnd = "』"; token.Type = NakoTokenType.STRING; break;

            case '"': stringEnd = "\""; token.Type = NakoTokenType.STRING_EX; break;

            case '`': stringEnd = "`"; token.Type = NakoTokenType.STRING; break;

            default: throw new NakoTokenizerException(/*TODO*/);
            }
            position++;
            // ヒアドキュメント文字列
            // S = 「「 ... 」」
            // S = 「「「 ... 」」」
            if (start == '「' || start == '『')
            {
                position--;
                stringEnd = "";
                while (!IsEOF)
                {
                    if (CurrentChar == '「')
                    {
                        token.Type = NakoTokenType.STRING_EX;
                        stringEnd += '」';
                        position++;
                    }
                    else if (CurrentChar == '『')
                    {
                        token.Type = NakoTokenType.STRING;
                        stringEnd += '』';
                        position++;
                    }
                    else
                    {
                        break;
                    }
                }
            }
            // 文字列の終端まで取得
            var  builder     = new StringBuilder();
            bool isSkipBlank = false; // 空白のスキップを行うかどうか

            while (!IsEOF)
            {
                if (Equals(stringEnd))
                {
                    position += stringEnd.Length;
                    break;
                }
                char c = CurrentCharRaw;
                position++;
                if (isSkipBlank)
                {
                    if (c == ' ' || c == ' ' || c == '\t')
                    {
                        continue;
                    }
                    isSkipBlank = false;
                }
                if (c == '\n')
                {
                    lineNo++;
                    isSkipBlank = true;
                }
                builder.Append(c);
            }
            token.Value = builder.ToString();
            CheckJosi(token);
            return(token);
        }
Пример #20
0
 /// <summary>
 /// 指定したエラー メッセージ、この例外の原因であるトークン、この例外の原因である内部例外への参照を使用して、NakoTokenizerException クラスの新しいインスタンスを初期化します。
 /// </summary>
 /// <param name="message">例外の原因を説明するエラー メッセージ。</param>
 /// <param name="token">例外の原因であるトークン。</param>
 /// <param name="innerException">現在の例外の原因である例外。innerException パラメータが null 参照でない場合は、内部例外を処理する catch ブロックで現在の例外が発生します。</param>
 public NakoTokenizerException(string message, NakoToken token, Exception innerException)
     : base(message, innerException)
 {
     Token = token;
 }
Пример #21
0
 /// <summary>
 /// 現在の位置から助詞が存在するかどうかを調査します。もし助詞が存在する場合は、指定したトークンに助詞を設定します。
 /// </summary>
 /// <param name="token">助詞を設定するトークン。</param>
 /// <returns>助詞が存在する場合は true。それ以外の場合は false。</returns>
 /// <remarks>
 /// このメソッドは、助詞が存在する場合、助詞の文字数だけ位置を進めます。
 /// </remarks>
 private bool CheckJosi(NakoToken token)
 {
     if (IsEOF)
     {
         return false;
     }
     // 助詞はひらがななので
     if (!NakoUtility.IsHiragana(CurrentChar))
     {
         return false;
     }
     // 助詞を 1 つずつ調べる
     foreach (string josi in NakoJosi.Instance)
     {
         if (this.Equals(josi))
         {
             token.Josi = josi;
             position += josi.Length;
             return true;
         }
     }
     return false;
 }
Пример #22
0
 /// <summary>
 /// 構文解析エラーを出す
 /// </summary>
 /// <param name="message"></param>
 /// <param name="tok"></param>
 public NakoParserException(string message, NakoToken tok)
     : base(message + ":" + tok.ToStringForDebug())
 {
 }
Пример #23
0
 /// <summary>
 /// 単語を辞書に追加する
 /// </summary>
 /// <param name="key">単語</param>
 /// <param name="type">単語の種類</param>
 public void AddWord(string key, NakoTokenType type)
 {
     key = NakoToken.TrimOkurigana(key);
     this._dictionary.Add(key, type);
 }
Пример #24
0
        /// <summary>
        /// 展開あり文字列トークンを再帰的に展開します。
        /// </summary>
        /// <param name="token">展開あり文字列トークン。</param>
        /// <returns>再帰的に展開したトークン一覧。</returns>
        private static NakoTokenList StringTokenExtract(NakoToken token)
        {
            var    tokens  = new NakoTokenList();
            string tmp     = "";
            string str     = token.Value;
            int    i       = 0;
            bool   isFirst = true;

            while (i < str.Length)
            {
                char ch = str[i];
                if (ch == '{' || ch == '{')
                {
                    if (isFirst)
                    {
                        isFirst = false;
                    }
                    else
                    {
                        // "&" トークンを追加
                        tokens.Add(new NakoToken(NakoTokenType.AND, token.LineNo, token.IndentLevel));
                    }
                    i++;
                    // 展開する文字列 ("{" と "}" との間) を取得する
                    string exString = "";
                    {
                        char end = (ch == '{') ? '}' : '}';
                        for (; i < str.Length; i++)
                        {
                            if (str[i] == end)
                            {
                                i++;
                                break;
                            }
                            exString += str[i];
                        }
                    }
                    // 文字列展開だけの特殊メソッド ('\' メソッド)
                    if (exString.Length > 0 && exString[0] == '\\')
                    {
                        if (exString == "\\t")
                        {
                            // \t の場合
                            tmp     += '\t';
                            exString = "";
                        }
                        else if (exString == "\\r")
                        {
                            // \r の場合
                            tmp     += '\r';
                            exString = "";
                        }
                        else if (exString == "\\n")
                        {
                            // \n の場合
                            tmp     += '\n';
                            exString = "";
                        }
                        else if (NakoUtility.IsNumber(exString[1]))
                        {
                            // \0 のような場合
                            exString = exString.Substring(1);
                            tmp     += (char)int.Parse(exString);
                            exString = "";
                        }
                        else if (exString[1] == '$')
                        {
                            // \$00 のような場合
                            exString = "0x" + exString.Substring(2);
                            tmp     += (char)int.Parse(exString);
                            exString = "";
                        }
                        else
                        {
                            new NakoTokenizerException("展開あり文字列内の利用できない`\\'メソッド:" + exString, token);
                        }
                    }
                    // 文字列展開だけの特殊メソッド ('~' メソッド)
                    else if (exString.Length == 1 && exString[0] == '~')
                    {
                        tmp     += "\r\n";
                        exString = "";
                    }
                    // 文字列トークンを追加
                    tokens.Add(new NakoToken(NakoTokenType.STRING, token.LineNo, token.IndentLevel, tmp));
                    tmp = "";
                    if (exString != "")
                    {
                        // "&" トークンを追加
                        tokens.Add(new NakoToken(NakoTokenType.AND, token.LineNo, token.IndentLevel));
                        // "(" トークンを追加
                        tokens.Add(new NakoToken(NakoTokenType.PARENTHESES_L, token.LineNo, token.IndentLevel));
                        // 再帰的にトークンを解析
                        var innerTokens = new NakoTokenizer().TokenizeSplitOnly(exString, token.LineNo, token.IndentLevel); // とりあえず区切るだけ
                        foreach (var innerToken in innerTokens)
                        {
                            tokens.Add(innerToken);
                        }
                        // ")" トークンを追加
                        tokens.Add(new NakoToken(NakoTokenType.PARENTHESES_R, token.LineNo, token.IndentLevel));
                    }
                    continue;
                }
                tmp += ch;
                i++;
            }
            if (tmp != "")
            {
                if (!isFirst)
                {
                    // "&" トークンを追加
                    tokens.Add(new NakoToken(NakoTokenType.AND, token.LineNo, token.IndentLevel));
                }
                // 文字列トークンを追加
                tokens.Add(new NakoToken(NakoTokenType.STRING, token.LineNo, token.IndentLevel, tmp));
            }
            else
            {
                // 必要なら空文字列トークンを追加
                if (isFirst)
                {
                    tokens.Add(new NakoToken(NakoTokenType.STRING, token.LineNo, token.IndentLevel, ""));
                }
            }
            // 助詞をコピー
            tokens[tokens.Count - 1].Josi = token.Josi;
            return(tokens);
        }
Пример #25
0
 /// <summary>
 /// 予約語のチェックと代入文への変換作業を行います。
 /// </summary>
 private void CheckWord()
 {
     // 予約語句のチェックなど
     tokens.MoveTop();
     for (; !tokens.IsEOF(); tokens.MoveNext())
     {
         // 予約語句の置き換え
         if (tokens.CurrentTokenType == NakoTokenType.WORD)
         {
             var    token = tokens.CurrentToken;
             string key   = token.GetValueAsName();
             if (tokenDic.ContainsKey(token.GetValueAsName()))
             {
                 token.Type = tokenDic [key];
             }
             else if (tokenDic.MayBeFunction(token.Value))
             {
                 tokens.Save();
                 StringBuilder tmp       = new StringBuilder();
                 int           indexFrom = tokens.IndexOf(token);
                 while (!tokens.IsEOF())
                 {
                     token = tokens.CurrentToken;
                     tmp.Append(token.Value);
                     if (tokenDic.IsFunction(tmp.ToString()))
                     {
                         NakoToken funcToken = new NakoToken(NakoTokenType.FUNCTION_NAME);
                         funcToken.Josi  = token.Josi;
                         funcToken.Value = tmp.ToString();
                         int indexTo = tokens.IndexOf(token);
                         for (int i = indexTo; i >= indexFrom; i--)
                         {
                             tokens.RemoveAt(i);
                         }
                         tokens.Insert(indexFrom, funcToken);
                         break;
                     }
                     else if (!tokenDic.MayBeFunction(tmp.ToString()))
                     {
                         tokens.Restore();
                         break;
                     }
                     tmp.Append(token.Josi);
                     tokens.MoveNext();
                 }
                 if (tokens.IsEOF())
                 {
                     return;
                 }
             }
         }
         // 助詞が「は」ならば、代入文に変える
         if (tokens.CurrentToken.Josi == "は")
         {
             tokens.CurrentToken.Josi = "";
             tokens.InsertAfterCurrentToken(new NakoToken(NakoTokenType.EQ));
         }
         // コメントならばトークンから取り除く
         if (tokens.CurrentTokenType == NakoTokenType.COMMENT)
         {
             tokens.RemoveCurrentToken();
             continue;
         }
     }
 }