目前尚不清楚您到底要做什么,但是 while 语句通常被“编译”成类似的东西
loop:
.. evaluate condition ..
JUMPC out
.. body ..
JUMP loop
out:
我通常对我写的解释器做的是有一个阶段来解决解析后的所有标签,以便在解析语法树时只发出跳转和带有虚拟名称的标签,然后可以计算每个标签的地址并调整所有其中,例如,您将拥有
510-580 .. evaluate condition ..
590 JUMPC 820
600-800 .. body ..
810 JUMP 510
820
这可以通过使用简单std::map
的存储所有标签来轻松完成,这样在生成解析树后,您只需浏览代码,计算标签的所有地址(从 AST 中消失),然后在跳转中插入正确的地址。
只是为了更清楚我通常做什么:
//parser.y
while_stat:
KW_WHILE T_LPAREN exp T_RPAREN block { $$ = new ASTWhileStat($3, $5); }
;
//ASTWhileStat.h
class ASTWhileStat : public ASTNode
{
ASTNode *m_condition;
ASTNode *m_body;
public:
ASTWhileStat(ASTNode *condition, ASTNode *body) {
m_condition = condition;
m_body = body;
}
ASTWhileStat(const ASTWhileStat &other) {
m_condition = other.m_condition;
m_body = other.m_body;
}
ASTWhileStat &operator= (const ASTWhileStat &other) {
if (&other != this) {
m_condition = other.m_condition;
m_body = other.m_body;
}
return *this;
}
ASTWhileStat *clone() {
return new ASTWhileStat(*this);
}
u8 type() {
return 0;
}
void populateSymbolTable() {
m_condition->populateSymbolTable();
if (m_body)
m_body->populateSymbolTable();
}
void generateASM()
{
u32 sCounter = ASTNode::labelCounter;
u32 eCounter = ++ASTNode::labelCounter;
++ASTNode::labelCounter;
printf("while%u:\n", sCounter);
m_condition->generateASM();
printf("NOT\n");
printf("JUMPC while%u\n", eCounter);
m_body->generateASM();
printf("JUMP while%u\n", sCounter);
printf("while%u:\n", eCounter);
++labelCounter;
}
}
现在,在我的例子中,我生成了一种低级语言的 ASM 文本输出,因为我有另一个解析器,它可以将这种 ASM 代码解析为二进制文件,但是你可以加入这两个步骤。ASM 解析器中发生的情况如下:
label:
STRING T_COLON { ASSEMBLER->addLabelHere($1); }
;
这样做:
void Assembler::addLabelHere(string str) {
printf("[ASSEMBLER] Added label %s at %u\n",str.c_str(),curPos);
labels[str] = curPos;
}
这是因为汇编器知道汇编代码的内部状态,并且知道当前距离程序开头的偏移量。
在组装阶段结束时,所有标签都已解决:
bool Assembler::solveLables()
{
map<string,u32>::iterator it, it2;
for (it = jumps.begin(); it != jumps.end(); ++it)
{
it2 = labels.find((*it).first);
if (it2 == labels.end())
{
printf("[ASSEMBLER] Unsolved label \"%s\"!\n", (*it).first.c_str());
return false;
}
u32 address = (*it2).second;
u32 pos = (*it).second;
printf("[ASSEMBLER] Solved jump at %u with address %u\n",pos,address);
memcpy(&code[pos], &address, sizeof(u32));
}
return true;
}
正如你所看到的,我在组装 JUMP 调用时将跳转的地址粗暴地复制到我保留的一些字节中。