[虚拟机]指令写出

是的, 还有几个节点的指令生成没有测试呢, 包括流控制语句, IO. 不过呢, 流控制是相对比较容易优化的, 进行"生成指令再对比"的测试不太好维护, 而 IO 指令很简单: 所以要不这样, 把指令写出来, 然后开个虚拟机运行, 如果运行没问题就算测试通过了.

语义错误处理

一些常见的语义错误处理在之前的之前的内容中都以注释形式出现, 不过如果留心的话, 上次更新的代码这些错误处理都是完备的, 比如在 jerry-ui.h 和 jerry-ui.c 两个文件中给出了这些错误的 UI, 而 const.h 中更新了很多字符串字面常量宏作为错误信息. 当然你也可以随意修改, 随便它们怎么运作. 这里只是稍带提一下, 以免等会儿突然见到会显得很突兀.

/* 一般语义错误 */
void semanticErr(ErrMsg err, int line)
{
    fprintf(stderr, "Error occurs at line %d:\n"
                    "    %s\n",
                    line, err);
    failed = 1;
}

/* 符号错误* /
void symError(ErrMsg err, struct VariableNode* var)
{
    fprintf(stderr, "Error occurs at variable `%s', line %d:\n"
                    "    %s\n",
                    var->ident, var->line, err);
    failed = 1;
}

参数处理

参数处理目前采用比较笨的方法, 就是逐个参数进行比较. 比如, 现在允许参数有

/* 输出帮助信息 */
#define OPTION_HELP_INFO "--help"
/* 指定目标文件 */
#define OPTION_OUTPUT_SPECIFY "-o"

参数解析循环

 

static int showHelp(int argc, char* argv[]);
static int setOutputFileName(int argc, char* argv[]);
static int setFileToProcess(int argc, char* argv[]);

void handleParam(int argc, char* argv[], FILE** out)
{
    int i = 1;
    if (1 == argc) {
        cmdParamError("Jerry: too few arguments.");
    }
    while (i < argc) {
        if (0 == strcmp(OPTION_HELP_INFO, argv[i])) {
            i += showHelp(argc - i - 1, argv + i + 1);

        } else if (0 == strcmp(OPTION_OUTPUT_SPECIFY, argv[i])) {
            i += setOutputFileName(argc - i - 1, argv + i + 1);

        } else {
            i += setFileToProcess(argc - i, argv + i);
        }
        ++i;
    }

    *out = fopen(outFileName, "wb");
    if (NULL == *out) {
        cmdParamError("output file access error.");
    }
}

每个分支匹配一个参数, 然后传入剩余的参数到具体的处理函数中去. 而每个处理函数的返回值则是它所消耗的参数个数, 以便于循环中调整参数指针.

 

指令写出

其它边边角角的东西不细说了, 现在切换到 jerry-compiler.c 文件中. main 函数中并没有写出的操作, 在调用了 handleParam 之后只是为语法分析进行铺垫工作和资源分配之类的. 嗯, 另有一个地方比 main 函数更适合完成这项工作, 那就是压在语法分析器栈栈底的无名英雄 FakeDefaultAnalyser 的 consumeNonTerminal 函数.

很显然, 当这个函数被调用时, 如果没有任何错误, 那么一定是其它所有分析器都完成任务正常退出了, 因此, 传递给它的语法节点就是表征整个源文件的语法树. 那么只需要将这个节点的指令序列输出就搞定了.

void fakeDefaultAnalyserConsumeNT(void* self, struct AbstractSyntaxNode* node)
{
    if (NULL == node) {
        printf("Nothing returned.\n");
    } else {
        node = (struct AbstractSyntaxNode*)newBasicBlockNode(node);
        initStack(&loopStack);
        initialSymTabManager();
        struct List* insList = node->createInstruction(node);
        finalizeSymTabManager();
        loopStack.finalize(&loopStack);
        if (isFailed()) {
            while (0 != insList->count(insList)) {
                revert(insList->popElementAt(insList, 0));
            }
            fprintf(stderr, "Error in source code.\n");
        } else {
            // 这里写出指令
    }
}
现在要做的是, 首先给每条指令烙上偏移值, 以便于让跳转指令运作 --- 跳转指令都会变为一条整数参数指令.

            InstructionCode code;
            int segOffset = 0;
            struct Iterator* iterator;
            struct NoParamInstruction* endProg = (struct NoParamInstruction*)
                                    allocate(sizeof(struct NoParamInstruction));
            endProg->code = END_PROGRAM;

            // 加入最后一条结束指令
            insList->addTo(insList, endProg, insList->count(insList));
            for_each (iterator, insList) {
                code = ((struct AbstractInstruction*)
                        (iterator->current(iterator)))->code;
                ((struct AbstractInstruction*)
                 (iterator->current(iterator)))->segOffset = segOffset;
                segOffset += sizeof(InstructionCode);

                // 计入参数偏移量
                if (isJump(code) || isIntParamIns(code)) {
                    segOffset += INT_SIZE;
                } else if (isRealParamIns(code)) {
                    segOffset += REAL_SIZE;
                }
            }
然后写出总的指令大小, 或者可以称之为山寨版的代码段总长度

            if (1 != fwrite(&segOffset, sizeof(segOffset), 1, treeout)) {
                fprintf(stderr,
                        "An error occurs while writing instructions to file.\n");
                exit(1);
            }
最后一步, 将每个指令写出

            for_each (iterator, insList) {
                // 实现在下面
                writeInstruction(iterator->current(iterator));
            }
            // 回收指令空间
            while (0 != insList->count(insList)) {
                revert(insList->popElementAt(insList, 0));
            }

/* 逐条指令写出 */
void writeInstruction(void* instruction)
{
    InstructionCode iCode = ((struct AbstractInstruction*)instruction)->code;
    if (1 != fwrite(&iCode, sizeof(InstructionCode), 1, treeout)) {
        fprintf(stderr, "Error occur when flush instructions to file.\n");
        exit(1);
    }
    // 带参数指令写出参数
    if (isIntParamIns(iCode)) {
        if (1 != fwrite(&(((struct IntParamInstruction*)instruction)->param),
                        INT_SIZE, 1, treeout)) {
            fprintf(stderr, "Error occur when flush instructions to file.\n");
            exit(1);
        }
    } else if (isRealParamIns(iCode)) {
        if (1 != fwrite(&(((struct RealParamInstruction*)instruction)->param),
                        REAL_SIZE, 1, treeout)) {
            fprintf(stderr, "Error occur when flush instructions to file.\n");
            exit(1);
        }
    } else if (isJump(iCode)) {
        // 将指针参数转换为段间偏移量, 写出该偏移量整数
        int offset = ((struct JumpInstruction*)instruction)
                                                ->targetIns->segOffset
                     - ((struct JumpInstruction*)instruction)->segOffset;
        if (1 != fwrite(&offset, INT_SIZE, 1, treeout)) {
            fprintf(stderr, "Error occur when flush instructions to file.\n");
            exit(1);
        }
    }
}

请 update Jerry 以获取最新源代码.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值