我正在尝试实施一个简单的基础,但我担心Bison的操作对我来说并不清楚。为了实现'while'循环,我发现的例子使用了下面的结构(或类似的):
while'('expression')'statement {$$ = opr(WHILE,2,$ 3,$ 5); }
但实际上有两个隐藏的跳转我应该分配给令牌。我的意思如下:
while // no token assigned
( expression ) // take appropriate sequence of tokens
jump back if not met // insert a conditional jump token - a jump to expression checking
statement // take appropriate sequence of tokens
jump unconditionally // insert a token to jump to expression checking
然而,表达长度和语句长度都不知道,我可以记录输入'while'关键字的地址,因此可以计算最后一次跳转。但是什么会激怒Bison插入条件跳转令牌?
答案 0 :(得分:1)
目前尚不清楚你要做的是什么,但是一段时间的陈述经常被“编译”成类似
的东西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调用时保留的某些字节内。