语法解析流程简述
lua之类的脚本语言先把源代码翻译成字节码,而后再使用虚拟机执行字节码。
而从源代码到字节码要经过多个过程:
词法解析,语法解析,语义解析,代码生成。
源代码->单词流->语法树->带语义的语法树->字节码。
语义解析主要是完善语法树,添加语义信息:
- 设置变量作用域。
- 设置break和continue对应的循环语句。
lua的官方实现做了优化,将4步流程压缩成一个。词法解析提供接口供语法解析调用,语法解析、语义解析压缩成一个流程,代码生成提供接口供语法解析使用。这也造成解析代码难以看懂修改。
luna的实现:词法解析还是提供接口,语法解析,语义解析,代码生成是分开的,就好懂很多了。(其中的观察者模式用的不适合)
oms的实现中语义解析合并代码生成中。
词法解析,语法解析,代码生成。
源代码->单词流->语法树->字节码。
指令生成
遍历语法树生成代码的过程比较无脑,对每个语法树节点类型写一个解析函数。
一个例子。
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);
}
}
指令系统和局部变量分配
lua的指令是带寄存器地址的,最多3地址,一个地址一个字节,也就限制了局部变量的总数不可能超过256个。
自己实现时,指令也是带寄存器地址的,最直接的好处是,指令能少很多。然后局部变量分配就是基于栈的了,简单方便。
简化的指令bit分配。
code: int32_t
A : uint8_t
B : uint8_t
C : uint8_t
Bx : int16_t (B+C)
局部变量每个函数单独分配,从0开始,定义一个局部变量就+1,退出block时回收当前作用域内的。
临时分配的也要及时回收,如
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);// 回收临时分配的局部变量。
}
语义分析部分
在遍历语法树时,要维护作用域树和循环结构链表。
//"while" exp "do" block "end"
void HandleWhileStatement(WhileStatement tree)
{
EnterLoop();// 进入循环
EnterBlock();// 进入新的作用域
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);// 变量循环block
LeaveBlock();// 退出作用域
// jump to loop head
code = Instruction.Bx(OpType.OpType_Jmp, 0);
index = f.AddInstruction(code, -1);
AddLoopJumpInfo(JumpType.JumpHead, index);
LeaveLoop();// 退出循环
}