/* vim: ts=4 sw=4 sts=4 ff=unix fenc=utf-8 : * ===================================================================== * kscript_token.c * Copyright (c) 2003 - 2011 sys0tem * LICENSE : * LGPL (GNU Lesser General Public License - Version 3,29 June 2007) * http://www.gnu.org/copyleft/lesser.html * or * EPL (Eclipse Public License - v1.0) * http://www.eclipse.org/legal/epl-v10.html * ===================================================================== */ #include <stdio.h> #include <sc_number.h> #include "kscript_token.h" /* シンボルの最大長 */ #define KSCRIPT_SYMBOL_LENGTH_MAX (8) /** * TokenTypeと対象タイプのシンボル文字列のペア定義用構造体。 */ typedef struct { enum TokenType type; char str[KSCRIPT_SYMBOL_LENGTH_MAX]; } KScriptTokenTypePair; /** * 同じシンボル文字で始まるシンボルをまとめた構造体 */ typedef struct { int count; KScriptTokenTypePair typePair[8]; } KScriptTokenTypeTableEntry; /** * トークンタイプマップ。 * * 最長一致で検索するために、文字列の短い順に定義すること。 */ static KScriptTokenTypeTableEntry KScript_tokenTypeTable[] = { { 8,{ { T_NOT , "!" }, { T_NOTE , "!=" }, { T_NOTLT , "!<" }, { T_NOTGT , "!>" }, { T_NOTLTE , "!<=" }, { T_NOTGTE , "!>=" }, { T_NOTLTGT , "!<>" }, { T_NOTLTGTE , "!<>=" } } }, { 8,{ { T_LT , "<" }, { T_ARROWL , "<-" }, { T_LTE , "<=" }, { T_LTD , "<<" }, { T_LTGT , "<>" }, { T_LTDE , "<<=" }, { T_LTGTE , "<>=" }, { T_HTMLCOMMENTS, "<!--" } } }, { 6,{ { T_GT , ">" }, { T_GTE , ">=" }, { T_GTD , ">>" }, { T_GTDE , ">>=" }, { T_GTT , ">>>" }, { T_GTTE , ">>>=" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" } } }, { 3,{ { T_DOT , "." }, { T_DOTD , ".." }, { T_DOTT , "..." }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" } } }, { 3,{ { T_AMP , "&" }, { T_AMPE , "&=" }, { T_AMPD , "&&" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" } } }, { 3,{ { T_PIPE , "|" }, { T_PIPEE , "|=" }, { T_PIPED , "||" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" } } }, { 5,{ { T_MINUS , "-" }, { T_MINUSE , "-=" }, { T_MINUSD , "--" }, { T_ARROWR , "->" }, { T_HTMLCOMMENTE, "-->" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" } } }, { 3,{ { T_PLUS , "+" }, { T_PLUSE , "+=" }, { T_PLUSD , "++" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" } } }, { 2,{ { T_SLASH , "/" }, { T_SLASHE , "/=" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" } } }, { 2,{ { T_STAR , "*" }, { T_STARE , "*=" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" } } }, { 2,{ { T_EQUAL , "=" }, { T_EQUALE , "==" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" } } }, { 2,{ { T_PARCENT , "%" }, { T_PARCENTE , "%=" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" } } }, { 2,{ { T_HAT , "^" }, { T_HATE , "^=" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" } } }, { 2,{ { T_TILD , "~" }, { T_TILD , "~=" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" } } }, { 2,{ { T_HASH , "#" }, { T_SHELLHEAD , "#!" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" } } }, { 1,{ { T_QUESTION , "?" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" } } }, { 1,{ { T_PARENL , "(" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" } } }, { 1,{ { T_PARENR , ")" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" } } }, { 1,{ { T_BRACKETL , "[" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" } } }, { 1,{ { T_BRACKETR , "]" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" } } }, { 1,{ { T_CURLYL , "{" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" } } }, { 1,{ { T_CURLYR , "}" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" } } }, { 1,{ { T_SEMICOLON , ";" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" } } }, { 1,{ { T_COLON , ":" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" } } }, { 1,{ { T_DOLLAR , "$" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" } } }, { 1,{ { T_AT , "@" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" } } }, { 1,{ { T_ESCAPE , "\\" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" }, { T_UNKNOWN , "" } } } }; /** 上記のテーブル位置検索用のキー */ static const char* KScript_tokenTypeTableKey = "!<>.&|-+/*=%^~#?()[]{};:$@\\"; /* --------------------------------------------------------------------- * プロトタイプ宣言 * --------------------------------------------------------------------- */ static bool KScript_parseComment(KScriptTokenStatus* status, int lineNum); static bool KScript_parseCommentStart(KScriptTokenStatus* status, int lineNum); static bool KScript_parseBlockComment(KScriptTokenStatus* status, int lineNum); static bool KScript_parseNestingBlockComment(KScriptTokenStatus* status, int lineNum); static bool KScript_parseSimpleSymbol(KScriptTokenStatus* status, int lineNum); static bool KScript_parseOtherSymbol(KScriptTokenStatus* status, int lineNum); static bool KScript_parseStringToken(KScriptTokenStatus* status, int lineNum); static bool KScript_parseNumberToken(KScriptTokenStatus* status, int lineNum); static bool KScript_parseKeywordSymbolToken(KScriptTokenStatus* status, int lineNum); static void KScript_addToken(KScriptTokenStatus* status, KScriptToken* token); static int KScript_getTableIndex(char c); static int KScript_indexOf(const char* ptr, char c); /** * 指定された行を解析し、トークンを token に追加します。 * * @param token トークン格納用 * @param line 行 * @param lineNum 行番号 * @param status ステータス */ void KScript_parseLine(KScriptTokenStatus* status, char* line, int lineNum) { bool ret; status->ptr = line; while ((*status->ptr) != '\0') { /* コメント解析 */ ret = KScript_parseComment(status, lineNum); if (ret) { continue; } /* 単純シンボル解析 */ ret = KScript_parseSimpleSymbol(status, lineNum); if (ret) { continue; } /* その他のシンボル解析 */ KScript_parseOtherSymbol(status, lineNum); } } /** * コメント解析。 * 指定された位置の文字列がコメントであれば true を返す。 * コメントが終了した場合、tokens にコメントトークンを追加します。 * * @param status ステータス * @param lineNum 行番号 */ static bool KScript_parseComment(KScriptTokenStatus* status, int lineNum) { bool isCommentStart; if (!status->isNowBlockComment && (status->nestingComment == 0)) { /* コメント中ではない⇒コメント開始かどうかチェックする */ isCommentStart = KScript_parseCommentStart(status, lineNum); if (!isCommentStart) { /* コメントではない */ return false; } } if (status->isNowBlockComment) { /* ブロックコメント */ KScript_parseBlockComment(status, lineNum); } else if (status->nestingComment > 0) { /* ネスト可能ブロックコメント */ KScript_parseNestingBlockComment(status, lineNum); } /* else { 行コメントだった (上で解析済み) } */ return true; } /** * 解析位置の文字列がコメント開始かどうかチェックします。 * コメント開始の場合、 * 行コメントの場合はコメントトークンを tokens に追加 * ブロックコメントの場合は、isNowComment を true に設定します。 * * @param status ステータス * @param lineNum 行番号 * @return true/false (コメント/コメント以外) */ static bool KScript_parseCommentStart(KScriptTokenStatus* status, int lineNum) { bool isComment = false; if ((*(status->ptr) == '/') && (*(status->ptr + 1) != '\0')) { switch (*(status->ptr + 1)) { case '/': /* 行コメント (本行の解析終了) */ *(status->ptr) = '\0'; isComment = true; break; case '*': /* ブロックコメント開始 */ status->isNowBlockComment = true; status->ptr += 2; isComment = true; case '+': /* ネスト可能なブロックコメント開始 */ status->nestingComment = 1; status->ptr += 2; isComment = true; break; default: isComment = false; } } return isComment; } /** * ブロックコメント解析。 * ブロックコメントの終了位置を探し、見つかれば、 * isNowBlocking を解除します。 * * @param status ステータス * @param lineNum 行番号 * @return true 固定 */ static bool KScript_parseBlockComment(KScriptTokenStatus* status, int lineNum) { char* ptr = strstr(status->ptr, "*/"); if (ptr == NULL) { /* 見つからない (本行の解析は終了) */ *(status->ptr) = '\0'; } else { /* 見つかった (コメント領域終了) */ status->ptr = (ptr + 2); status->isNowBlockComment = false; } return true; } /** * ネスト可能なブロックコメント解析。 * * @param status ステータス * @param lineNum 行番号 * @return true 固定 */ static bool KScript_parseNestingBlockComment(KScriptTokenStatus* status, int lineNum) { char* startPtr = strstr(status->ptr, "/+"); char* endPtr = strstr(status->ptr, "+/"); if (endPtr == NULL) { /* コメント終了位置が見つからない */ if (startPtr != NULL) { /* 更なるネスト */ status->nestingComment++; } /* 本行の解析終了 */ *(status->ptr) = '\0'; } else { /* コメント終了位置が見つかった */ if ((startPtr == NULL) || (startPtr > endPtr)) { /* とりあえず一旦コメント終了 */ status->nestingComment--; } status->ptr = (endPtr + 2); } return true; } /** * 単純シンボルトークンを解析します。 * * @param status ステータス * @param lineNum 行番号 * @return true/false (単純シンボルトークン/以外) */ static bool KScript_parseSimpleSymbol(KScriptTokenStatus* status, int lineNum) { int tblIndex; int searchIndex; int tmpRet; int tmpLen; KScriptTokenTypePair* pair; KScriptToken token; tblIndex = KScript_getTableIndex(*(status->ptr)); if (tblIndex < 0) { return false; } searchIndex = KScript_tokenTypeTable[tblIndex].count - 1; while (searchIndex >= 0) { pair = &KScript_tokenTypeTable[tblIndex].typePair[searchIndex]; tmpLen = strlen(pair->str); tmpRet = strncmp(pair->str, status->ptr, tmpLen); if (tmpRet == 0) { /* シンボルあり */ token.tokenType = pair->type; token.tokenKeyword = K_UNKNOWN; token.tokenString = NULL; token.lineNumber = lineNum; KScript_addToken(status, &token); status->ptr += tmpLen; return true; } searchIndex--; } /* シンボル見つからず */ return false; } /** * 以下のシンボルトークンを生成します。 * <pre> * 識別子 * 文字列リテラル * 文字リテラル * 整数リテラル * 浮動小数リテラル * キーワード * </pre> * @param status ステータス * @param lineNum 行番号 * @return true/false (成功/不正なシンボルを含む) */ static bool KScript_parseOtherSymbol(KScriptTokenStatus* status, int lineNum) { bool ret; switch (*status->ptr) { case '\'': case '"': case '`': /* 文字、文字列トークン */ ret = KScript_parseStringToken(status, lineNum); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': ret = KScript_parseNumberToken(status, lineNum); break; default: /* その他 */ ret = KScript_parseKeywordSymbolToken(status, lineNum); } return ret; } /** * 文字列トークンを解析します。 * * @param status ステータス * @param lineNum 行番号 * @return true/false (成功/文字列トークン以外) */ static bool KScript_parseStringToken(KScriptTokenStatus* status, int lineNum) { KScriptToken token; int pos; const char* bkupPtr; char qchar = *(status->ptr); if (qchar == '\'') { token.tokenType = T_CHARACTER; } else if (qchar == '`') { token.tokenType = T_COMMAND; } else { token.tokenType = T_STRING; } /* 区切り位置はtokenStringに含めない */ status->ptr++; bkupPtr = status->ptr; pos = KScript_indexOf(status->ptr, qchar); if (pos < 0) { /* 見つからない。 */ /* TODO. log */ return false; } /* 区切り文字位置を'\0'に置き換える */ status->ptr += pos; *(status->ptr) = '\0'; status->ptr++; token.tokenKeyword = K_UNKNOWN; token.tokenString = SC_String_new(pos); token.tokenString->append(token.tokenString, bkupPtr); /* TODO token.tokenString->toNative(token.tokenString); */ token.lineNumber = lineNum; KScript_addToken(status, &token); return true; } /** * 数値を解析します。 * * @param status ステータス * @param lineNum 行番号 * @return true/false (成功/失敗) */ static bool KScript_parseNumberToken(KScriptTokenStatus* status, int lineNum) { /* KScriptToken token; const char* bkupPtr = status->ptr; bool isDot = false; while (*status->ptr != '\0') { switch (*(status->ptr)) { case 'x': case } } */ return true; } /** * キーワードまたは、ユーザ定義シンボルを解析します。 * * @param status ステータス * @param lineNum 行番号 * @return true/false (成功/キーワード、ユーザ定義シンボル以外) */ static bool KScript_parseKeywordSymbolToken(KScriptTokenStatus* status, int lineNum) { KScriptToken token; const char* bkupPtr = status->ptr; char bkupChar; int len; while (*(status->ptr) != '\0') { if ((('a' <= *(status->ptr) && (*(status->ptr) <= 'z'))) || (('A' <= *(status->ptr) && (*(status->ptr) <= 'Z'))) || (('0' <= *(status->ptr) && (*(status->ptr) <= '9'))) || ('_' == *(status->ptr))) { // NOP } else { /* シンボル以外の文字が含まれる */ break; } status->ptr++; } if (bkupPtr == status->ptr) { /* 空白やその他の文字 */ status->ptr++; return false; } /* TODO */ bkupChar = *(status->ptr); *(status->ptr) = '\0'; len = strlen(bkupPtr); token.tokenType = T_KEYWORD; token.tokenType = T_IDENTIFIER; token.tokenKeyword = K_UNKNOWN; token.tokenString = SC_String_new(len + 1); token.tokenString->append(token.tokenString, bkupPtr); token.lineNumber = lineNum; *(status->ptr) = bkupChar; KScript_addToken(status, &token); return true; } /** * Tokenを追加します。 * * @param status TokenStatus * @param token 追加するトークン */ static void KScript_addToken(KScriptTokenStatus* status, KScriptToken* token) { status->token->add(status->token, -1, token, sizeof(KScriptToken)); } /** * 指定された文字のシンボルテーブル上のインデックスを取得します。 * * @param c 文字 * @return インデックス */ static int KScript_getTableIndex(char c) { static int max = 0; int i; if (max == 0) { max = strlen(KScript_tokenTypeTableKey); } for (i = 0; i < max; i++) { if (KScript_tokenTypeTableKey[i] == c) { return i; } } return -1; } /** * 文字列 ptr の中より、文字 c を検索し、その位置を返します。 * * @param ptr 文字列 * @param c 検索文字 * @return 位置、見つからない場合-1 */ static int KScript_indexOf(const char* ptr, char c) { const char* tmpPtr = ptr; while (*tmpPtr != '\0') { if (*tmpPtr == c) { return (tmpPtr - ptr); } tmpPtr++; } return -1; }