tokenizer分詞器众雷,是Parser解析工具的核心邏輯工具,主要工作是將rc文件的字符串分解出令牌和單詞坐求。
/system/core/init/tokenizer.h
/system/core/init/tokenizer.cpp
token 令牌
token令牌是調(diào)用tokenizer.next_token()
的返回值奸笤,表示解析到需要調(diào)用者處理的新事件。
#define T_EOF 0
#define T_TEXT 1
#define T_NEWLINE 2
T_EOF
解析完成
字符串已解析到末端霎槐。end-of-file,rc文件完成解析梦谜。T_TEXT
解析到新單詞
tokenizer解析到一個單詞栽燕。單詞的地址保存在傳入的parse_state結(jié)構(gòu)體中。T_NEWLINE
解析完成一行
tokenizer解析到換行符改淑。表示當(dāng)前行解析結(jié)束碍岔,準(zhǔn)備解析下一行。
parse_state 解析數(shù)據(jù)結(jié)構(gòu)體
parse_state
結(jié)構(gòu)體用于存放 解析過程的狀態(tài) 和 產(chǎn)生的臨時數(shù)據(jù)朵夏。
struct parse_state
{
char *ptr;
char *text;
int line;
int nexttoken;
};
char *ptr
正在解析字符的指針
tokenizer當(dāng)前正在解析的字符指針蔼啦,相當(dāng)于解析進(jìn)度。char *text
詞文本指針
tokenizer檢出的單詞的首字符指針仰猖。int line
文本行號
tokenizer當(dāng)前正在解析的數(shù)據(jù)的行號捏肢。int nexttoken
令牌緩存
優(yōu)化解析速度。在檢出一個單詞的過程中饥侵,解析到了換行符鸵赫,意味著單詞結(jié)束并產(chǎn)生換行,需要輸出T_TEXT
和T_NEWLINE
躏升。把后者緩存到nexttoken
辩棒,在下一次next_token()
函數(shù)的早段邏輯中直接返回。
next_token() 執(zhí)行解析
/system/core/init/tokenizer.cpp
next_token()
函數(shù)有兩個關(guān)鍵的局部變量
int next_token(struct parse_state *state) {
char *x = state->ptr;
char *s;
...
}
char *x
當(dāng)前正在解析的字符指針
在調(diào)用函數(shù)時,從傳入結(jié)構(gòu)體中獲取一睁,在產(chǎn)生令牌時钻弄,儲存回結(jié)構(gòu)體。char *s
當(dāng)前檢出中的單詞的尾部字符指針
指向檢出中單詞的最后一個字符的字符指針者吁。檢出過程中窘俺,tokenizer認(rèn)為*x
是當(dāng)前檢出單詞的一部分時,通過*s++ = *x++
复凳,把*x
覆蓋到*s
瘤泪,然后各自自增指向下一數(shù)據(jù)地址。
next_token()
函數(shù)邏輯分為非單詞檢出和單詞檢出兩個部分育八,由于內(nèi)容比較緊湊均芽,邏輯解析直接寫到源碼注釋。
非單詞檢出流程:
int next_token(struct parse_state *state) {
// 當(dāng)前識別位置
char *x = state->ptr;
// 單詞末端位置
char *s;
// 緩存令牌, 用于優(yōu)化效率
if (state->nexttoken) {
int t = state->nexttoken;
state->nexttoken = 0;
return t;
}
// 非單詞檢出階段
for (;;) {
switch (*x) {
// '\0', 即NULL字符
// :字符串結(jié)尾
// :當(dāng)前識別位置(+1)(記錄), 返回T_EOF
case 0:
state->ptr = x;
return T_EOF;
// 換行
// :行解析結(jié)束
// :當(dāng)前識別位置(+1)(記錄), 返回T_NEWLINE
case '\n':
x++;
state->ptr = x;
return T_NEWLINE;
// 空格, 制表符, 回車
// :無效字符
// :當(dāng)前識別位置(+1), 繼續(xù)循環(huán)
case ' ':
case '\t':
case '\r':
x++;
continue;
// #號
// :注釋, 跳過該行所有字符直到
// - 換行 : 該行解析結(jié)束, 當(dāng)前識別位置(+1)(記錄), 返回T_NEWLINE
// - NULL字符 : 文件解析結(jié)束, 當(dāng)前識別位置, 返回T_EOF
case '#':
while (*x && (*x != '\n')) x++;
if (*x == '\n') {
state->ptr = x+1;
return T_NEWLINE;
} else {
state->ptr = x;
return T_EOF;
}
// 其他字符
// :識別為一個詞的首部
// :開始單詞檢出流程
default:
goto text;
}
}
...
}
單詞檢出流程:
int next_token(struct parse_state *state) {
...
// 檢出單詞单鹿,返回T_TEXT令牌
// 單詞末端位置寫入NULL字符, 剪裁出字符串
// 當(dāng)前識別位置(記錄), 返回T_TEXT
textdone:
state->ptr = x; // 當(dāng)前識別位置保存到state
*s = 0; // 單詞末端位置寫入NULL字符
return T_TEXT;
// 單詞檢出流程 (初始化單詞的首尾指針)
text:
state->text = s = x; // 初始化單詞的首尾指針為當(dāng)前識別位置
// 單詞檢出流程
textresume:
for (;;) {
switch (*x) {
// '\0', 即NULL字符
// :字符串結(jié)尾
// :檢出單詞
case 0:
goto textdone;
// 空格, 制表符, 回車
// :詞結(jié)尾
// :當(dāng)前識別位置(+1), 檢出單詞
case ' ':
case '\t':
case '\r':
x++;
goto textdone;
// 換行
// :行解析結(jié)束
// :當(dāng)前識別位置(+1), 檢出單詞, 并緩存T_NEWLINE
case '\n':
state->nexttoken = T_NEWLINE;
x++;
goto textdone;
// 引號(左則)
// 被引號包裹的字符串視為一個整體進(jìn)行處理
case '"':
// 當(dāng)前識別位置(+1), 即跳過引號
x++;
for (;;) {
switch (*x) {
// :文件解析結(jié)束
// :丟棄當(dāng)前解析中的單詞, 返回T_EOF
case 0:
state->ptr = x;
return T_EOF;
// 引號(右則)
// :跳到單詞檢出流程
// :正常情況下, 會檢出單詞, 跳到textdone
case '"':
x++;
goto textresume;
// 其他字符
// :單詞尾端寫入當(dāng)前字符
default:
*s++ = *x++;
}
}
break;
// 處理轉(zhuǎn)義字符
case '\\':
// 當(dāng)前識別位置(+1), 即跳過當(dāng)前轉(zhuǎn)義字符
x++;
switch (*x) {
// '\0'
// :字符串結(jié)尾
// :跳到單詞檢出流程
case 0:
goto textdone;
// 需要寫入的轉(zhuǎn)移字符
// :單詞尾端寫入對應(yīng)字符
case 'n':
*s++ = '\n';
break;
case 'r':
*s++ = '\r';
break;
case 't':
*s++ = '\t';
break;
case '\\':
*s++ = '\\';
break;
// \\r
// :回車
// :"\\r\n"則換行, 否則跳過字符
case '\r':
if (x[1] != '\n') {
x++;
continue;
}
// '\n'
// :換行
// :行號(+1), 跳過接下來的空格或制表符
case '\n':
state->line++;
x++;
while((*x == ' ') || (*x == '\t')) x++;
continue;
// 其他轉(zhuǎn)移字符
// :直接在單詞尾端寫入當(dāng)前字符
default:
*s++ = *x++;
}
continue;
// 其他字符
// :直接在單詞尾端寫入當(dāng)前字符
default:
*s++ = *x++;
}
}
return T_EOF;
}