語法解析流程簡(jiǎn)述
lua之類的腳本語言先把源代碼翻譯成字節(jié)碼,而后再使用虛擬機(jī)執(zhí)行字節(jié)碼。
而從源代碼到字節(jié)碼要經(jīng)過多個(gè)過程:
詞法解析,語法解析,語義解析,代碼生成。
源代碼->單詞流->語法樹->帶語義的語法樹->字節(jié)碼。
語義解析主要是完善語法樹,添加語義信息:
- 設(shè)置變量作用域。
- 設(shè)置break和continue對(duì)應(yīng)的循環(huán)語句。
lua的官方實(shí)現(xiàn)做了優(yōu)化,將4步流程壓縮成一個(gè)。詞法解析提供接口供語法解析調(diào)用,語法解析、語義解析壓縮成一個(gè)流程,代碼生成提供接口供語法解析使用。這也造成解析代碼難以看懂修改。
luna的實(shí)現(xiàn):詞法解析還是提供接口,語法解析,語義解析,代碼生成是分開的,就好懂很多了。(其中的觀察者模式用的不適合)
oms的實(shí)現(xiàn)中語義解析合并代碼生成中。
詞法解析,語法解析,代碼生成。
源代碼->單詞流->語法樹->字節(jié)碼。
指令生成
遍歷語法樹生成代碼的過程比較無腦,對(duì)每個(gè)語法樹節(jié)點(diǎn)類型寫一個(gè)解析函數(shù)。
一個(gè)例子。
void HandleBlock(Block tree)
{
foreach(var stmt in tree.statements)
{
if (stmt is DoStatement)
HandleDoStatement(stmt as DoStatement);
else if (stmt is WhileStatement)
HandleWhileStatement(stmt as WhileStatement);
else if (stmt is IfStatement)
HandleIfStatement(stmt as IfStatement);
else if (stmt is ForStatement)
HandleForStatement(stmt as ForStatement);
else if (stmt is ForEachStatement)
HandleForEachStatement(stmt as ForEachStatement);
else if (stmt is ForInStatement)
HandleForInStatement(stmt as ForInStatement);
else if (stmt is FunctionStatement)
HandleFunctionStatement(stmt as FunctionStatement);
else if (stmt is LocalFunctionStatement)
HandleLocalFunctionStatement(stmt as LocalFunctionStatement);
else if (stmt is LocalNameListStatement)
HandleLocalNameListStatement(stmt as LocalNameListStatement);
else if (stmt is ReturnStatement)
HandleReturnStatement(stmt as ReturnStatement);
else if (stmt is BreakStatement)
HandleBreakStatement(stmt as BreakStatement);
else if (stmt is ContinueStatement)
HandleContinueStatement(stmt as ContinueStatement);
else if (stmt is AssignStatement)
HandleAssignStatement(stmt as AssignStatement);
else
HandleExpRead(stmt);
}
}
指令系統(tǒng)和局部變量分配
lua的指令是帶寄存器地址的,最多3地址,一個(gè)地址一個(gè)字節(jié),也就限制了局部變量的總數(shù)不可能超過256個(gè)。
自己實(shí)現(xiàn)時(shí),指令也是帶寄存器地址的,最直接的好處是,指令能少很多。然后局部變量分配就是基于棧的了,簡(jiǎn)單方便。
簡(jiǎn)化的指令bit分配。
code: int32_t
A : uint8_t
B : uint8_t
C : uint8_t
Bx : int16_t (B+C)
局部變量每個(gè)函數(shù)單獨(dú)分配,從0開始,定義一個(gè)局部變量就+1,退出block時(shí)回收當(dāng)前作用域內(nèi)的。
臨時(shí)分配的也要及時(shí)回收,如
void HandleAssignStatement(AssignStatement tree)
{
HandleExpList(tree.exp_list, tree.var_list.Count);
// var list
int register = GetNextRegisterId();
ResetRegisterId(register + tree.var_list.Count);
for(int i = 0; i < tree.var_list.Count; ++i)
{
HandleVarWrite(tree.var_list[i], register + i);
}
ResetRegisterId(register);// 回收臨時(shí)分配的局部變量。
}
語義分析部分
在遍歷語法樹時(shí),要維護(hù)作用域樹和循環(huán)結(jié)構(gòu)鏈表。
//"while" exp "do" block "end"
void HandleWhileStatement(WhileStatement tree)
{
EnterLoop();// 進(jìn)入循環(huán)
EnterBlock();// 進(jìn)入新的作用域
HandleExpRead(tree.exp);
// jump to loop tail when expression return false
var f = GetCurrentFunction();
var code = Instruction.ABx(OpType.OpType_JmpFalse, GetNextRegisterId(), 0);
int index = f.AddInstruction(code, -1);
AddLoopJumpInfo(JumpType.JumpTail, index);
HandleBlock(tree.block);// 變量循環(huán)block
LeaveBlock();// 退出作用域
// jump to loop head
code = Instruction.Bx(OpType.OpType_Jmp, 0);
index = f.AddInstruction(code, -1);
AddLoopJumpInfo(JumpType.JumpHead, index);
LeaveLoop();// 退出循環(huán)
}