是的, 还有几个节点的指令生成没有测试呢, 包括流控制语句, 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 以获取最新源代码.
2813

被折叠的 条评论
为什么被折叠?



