Parser是rc文件解析成執(zhí)行邏輯的核心工具。
內(nèi)部通過(guò)tokenizer分詞器對(duì)rc文件的字符流進(jìn)行解析,轉(zhuǎn)換成單詞(參數(shù))和對(duì)應(yīng)的token令牌。根據(jù)token令牌,派分到不同的解析器實(shí)現(xiàn)進(jìn)行的處理。
通過(guò)模板模式,Parser將實(shí)際的解析邏輯解耦到解析器模板的實(shí)現(xiàn)類中進(jìn)行。
Parser定義
class Parser {
public:
using LineCallback = std::function<Result<Success>(std::vector<std::string>&&)>;
Parser();
bool ParseConfig(const std::string& path);
void AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser);
void AddSingleLineParser(const std::string& prefix, LineCallback callback);
private:
std::map<std::string, std::unique_ptr<SectionParser>> section_parsers_;
std::vector<std::pair<std::string, LineCallback>> line_callbacks_;
};
bool ParseConfig(const std::string& path);
解析指定的rc文件
path可以是rc文件路徑,也可以是一個(gè)目錄路徑。void AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser);
添加 SectionParser 解析器
添加段落解析器,name為一行命令中,首個(gè)參數(shù)的完全匹配值。void AddSingleLineParser(const std::string& prefix, LineCallback callback);
添加 LineCallback 解析器
添加單行命令解析器。prefix為一行命令中,首個(gè)參數(shù)的前序匹配值。
單行命令解析器模板:LineCallback
class Parser {
public:
using LineCallback = std::function<Result<Success>(std::vector<std::string>&&)>;
}
本質(zhì)是一個(gè)函數(shù)。
解析過(guò)程中,對(duì)一行命令的首個(gè)參數(shù)進(jìn)行匹配,當(dāng)匹配成功時(shí),馬上調(diào)用對(duì)應(yīng)的單行命令解析器(即回調(diào)函數(shù))。
從源碼上看,只有在ueventd進(jìn)程解析ueventd.rc時(shí),才有該類型解析器的應(yīng)用。
段落命令解析器模板:SectionParser
class SectionParser {
public:
virtual ~SectionParser() {}
virtual Result<Success> ParseSection(std::vector<std::string>&& args,
const std::string& filename, int line) = 0;
virtual Result<Success> ParseLineSection(std::vector<std::string>&&, int) { return Success(); };
virtual Result<Success> EndSection() { return Success(); };
virtual void EndFile(){};
};
解析過(guò)程中,對(duì)一行命令的首個(gè)參數(shù)進(jìn)行匹配,當(dāng)匹配成功時(shí),即認(rèn)為該行時(shí)一個(gè)命令段落的開(kāi)始,并使用匹配到的解析器解析段落中的命令內(nèi)容。
主要定義了幾個(gè)函數(shù):
-
ParseSection()
處理段落首行命令
行命令處理時(shí),匹配到段落解析器的關(guān)鍵字,則視為一個(gè)段落的首行,段落的后續(xù)命令交由該段落解析器處理,并調(diào)用該解析器的ParseSection()函數(shù)。
ParseLineSection()
處理段落內(nèi)容命令
對(duì)匹配不到關(guān)鍵字的行命令,視為段落的內(nèi)容,假如存在解析中的段落解析器,則調(diào)用該解析器的ParseLineSection()函數(shù)。EndSection()
處理段落結(jié)束
段落結(jié)束時(shí),假如存在解析中的段落解析器,則調(diào)用該解析器的EndSection()函數(shù)。EndFile()
處理文件結(jié)束
文件解析結(jié)束時(shí),調(diào)用所有已添加解析器中的EndFile()函數(shù)。
Parser實(shí)現(xiàn)
-
AddSectionParser/AddSingleLineParservoid Parser::AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser) { section_parsers_[name] = std::move(parser); } void Parser::AddSingleLineParser(const std::string& prefix, LineCallback callback) { line_callbacks_.emplace_back(prefix, callback); }把關(guān)鍵字和解析器添加到對(duì)應(yīng)的容器中。
-
ParseConfigParser::ParseConfig(const std::string& path) { size_t parse_errors; return ParseConfig(path, &parse_errors); } bool Parser::ParseConfig(const std::string& path, size_t* parse_errors) { *parse_errors = 0; if (is_dir(path.c_str())) { return ParseConfigDir(path, parse_errors); } return ParseConfigFile(path, parse_errors); }判斷
path是否文件夾,選擇直接解析文件,還是解析目錄下的所有rc文件。
ParseConfigDir()的內(nèi)部遍歷出的文件,最終同樣調(diào)用ParseConfigFile()進(jìn)行解析。 -
ParseConfigFilebool Parser::ParseConfigFile(const std::string& path, size_t* parse_errors) { auto config_contents = ReadFile(path); if (!config_contents) { return false; } config_contents->push_back('\n'); ParseData(path, *config_contents, parse_errors); for (const auto& [section_name, section_parser] : section_parsers_) { section_parser->EndFile(); } return true; }通過(guò)
ReadFile()函數(shù),將文件數(shù)據(jù)讀取為字符串?dāng)?shù)據(jù)。然后通過(guò)ParseData()函數(shù)執(zhí)行解析和處理。
文件解析完成時(shí),調(diào)用所有添加的解析器的EndFile()函數(shù)。
ParseData() 核心解析函數(shù)
Parse解析器的核心解析函數(shù)。
通過(guò)tokenizer分詞器解析字符串獲取token令牌,根據(jù)token令牌的類型,執(zhí)行處理邏輯,或派分事件到具體解析器實(shí)現(xiàn)進(jìn)行處理。
解析過(guò)程相關(guān)變量:
void Parser::ParseData(const std::string& filename, const std::string& data, size_t* parse_errors) {
std::vector<char> data_copy(data.begin(), data.end());
data_copy.push_back('\0');
...
}
-
data_copy
存放rc文件字符流的字符容器
把傳入數(shù)據(jù)復(fù)制到名為data_copy的vector<char>容器,并在末尾補(bǔ)充結(jié)束字符'\0'(NULL字符)。
struct parse_state
{
char *ptr; // 當(dāng)前解析中字符的指針,即解析進(jìn)度
char *text; // 當(dāng)前檢出的單詞,即參數(shù)
int line; // 當(dāng)前解析中的行號(hào)
int nexttoken; // 令牌緩存,用于性能優(yōu)化
};
ParseData() {
...
parse_state state;
state.line = 0;
state.ptr = &data_copy[0];
state.nexttoken = 0;
...
}
-
state
解析數(shù)據(jù)結(jié)構(gòu)體
創(chuàng)建parse_state結(jié)構(gòu)體state并初始化。用于存放 解析過(guò)程的狀態(tài) 和 產(chǎn)生的臨時(shí)數(shù)據(jù)。
ParseData() {
...
SectionParser* section_parser = nullptr;
int section_start_line = -1;
std::vector<std::string> args;
...
}
section_parser
段落目標(biāo)解析器
當(dāng)解析到新行時(shí),假如新行首個(gè)參數(shù)命中解析器關(guān)鍵字 (即段落的首行),則把命中的解析器設(shè)置為段落目標(biāo)解析器 (即當(dāng)前段落的解析器)。
沒(méi)有命中解析器關(guān)鍵字時(shí),則嘗試使用段落目標(biāo)解析器對(duì)行進(jìn)行解析(即解析段落非首行數(shù)據(jù),即命令數(shù)據(jù))。section_start_line
當(dāng)前段落開(kāi)始行號(hào)
如果當(dāng)前解析的行,是一個(gè)段落的首行,則記錄該行行號(hào)。args
當(dāng)前行解析出的參數(shù)鏈表
參數(shù)鏈表,存放一行命令解析出的所有參數(shù)。
ParseData() {
...
auto end_section = [&] {
if (section_parser == nullptr) return;
if (auto result = section_parser->EndSection(); !result) {
(*parse_errors)++;
}
section_parser = nullptr;
section_start_line = -1;
};
...
}
-
end_section
Lambda表達(dá)式,用于結(jié)束段落解析和重置解析相關(guān)數(shù)據(jù)
假如當(dāng)前段落目標(biāo)解析器不為空,則調(diào)用其EndSection()函數(shù),通知解析器該段落解析結(jié)束,并重置段落目標(biāo)解析器的指針section_parser和當(dāng)前段落開(kāi)始行號(hào)section_start_line。
解析過(guò)程邏輯階段:
tokenizer分詞器的實(shí)現(xiàn)分析:安卓啟動(dòng)流程(三) - tokenizer分詞器
ParseData() {
...
for (;;) {
switch (next_token(&state)) {
case T_EOF: ...
case T_NEWLINE: ...
case T_TEXT: ...
}
}
...
}
死循環(huán),通過(guò)next_token(&state)函數(shù)獲取token令牌和檢出單詞參數(shù)。
-
case T_TEXTcase T_TEXT: args.emplace_back(state.text); break;檢出了一個(gè)單詞
向args插入單詞,state.text指針指向單詞的首字符。 -
case T_EOFcase T_EOF: end_section(); return;表示rc文本解析結(jié)束
調(diào)用end_section表達(dá)式,通知當(dāng)前段落目標(biāo)解析器該段落解析結(jié)束,并退出ParseData函數(shù)。 -
case T_NEWLINE:case T_NEWLINE: // 當(dāng)前解析的行號(hào) +1 state.line++; // 檢出的參數(shù)為空,不執(zhí)行處理 if (args.empty()) break; // 該行是否匹配到單行命令解析器 // 假如匹配到單行命令解析器,嘗試通過(guò)end_section函數(shù)結(jié)束當(dāng)前段落解析, // 然后調(diào)用對(duì)應(yīng)的單行命令解析器 for (const auto& [prefix, callback] : line_callbacks_) { if (android::base::StartsWith(args[0], prefix)) { // 嘗試通過(guò)end_section函數(shù)結(jié)束當(dāng)前段落解析 end_section(); // 調(diào)用對(duì)應(yīng)的單行命令解析器 if (auto result = callback(std::move(args)); !result) { (*parse_errors)++; } break; } } // 該行是否匹配到段落解析器 if (section_parsers_.count(args[0])) { // 當(dāng)匹配到解析器時(shí), 嘗試通過(guò)end_section函數(shù)結(jié)束上一個(gè)段落解析 end_section(); // 記錄當(dāng)前解析器 section_parser = section_parsers_[args[0]].get(); // 記錄段落行號(hào) section_start_line = state.line; // 調(diào)用解析器的ParseSection函數(shù) if (auto result = section_parser->ParseSection(std::move(args), filename, state.line); !result) { (*parse_errors)++; section_parser = nullptr; } } else if (section_parser) { // 假如存在段落目標(biāo)解析器,則調(diào)用解析器的ParseLineSection函數(shù) if (auto result = section_parser->ParseLineSection(std::move(args), state.line); !result) { (*parse_errors)++; } } // 清空參數(shù)鏈表 args.clear(); break; }檢測(cè)到換行,該行結(jié)束,處理行命令
該行參數(shù)已解析完成,在解析下一行前,處理該行的命令。
大致流程總結(jié)
- 使用
ParseData()對(duì)路徑參數(shù)path下的所有rc文件,進(jìn)行解析。 -
ParseData()中命令的單位為每一行的文本。一行命令中檢出的所有參數(shù)都會(huì)放入args鏈表,在下一行解析開(kāi)始前,處理命令,并在命令處理完成時(shí)清空args鏈表。 - 命令處理開(kāi)始時(shí),先進(jìn)行解析器匹配。當(dāng)
args鏈表數(shù)量大于0時(shí),進(jìn)入匹配階段,首先匹配單行命令解析器,然后匹配段落命令解析器。 - 匹配階段無(wú)法匹配到解析器時(shí),則該行命令視為段落命令,進(jìn)入段落命令處理階段,嘗試使用已設(shè)置為處理中的段落解析器進(jìn)行處理,無(wú)法找到解析器,則跳過(guò)該行命令的處理。