何をしようとしているのかは明確ではありませんが、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テキスト出力を生成しますが、これら2つのステップを結合するだけで済みます。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呼び出しをアセンブルしたときに予約したいくつかのバイト内のジャンプのアドレスを残酷にコピーします。