代码生成

语法解析流程简述

lua之类的脚本语言先把源代码翻译成字节码,而后再使用虚拟机执行字节码。
而从源代码到字节码要经过多个过程:

词法解析,语法解析,语义解析,代码生成。
源代码->单词流->语法树->带语义的语法树->字节码。

语义解析主要是完善语法树,添加语义信息:

  1. 设置变量作用域。
  2. 设置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();// 退出循环
}

你可能感兴趣的:(代码生成)