0

シンプルなベーシックを実装しようとしていますが、バイソンの操作がはっきりしないのではないかと思います。'while'ループを実装するために、私が見つけた例では、以下の構造(または同様のもの)を使用しています。

while'('式')'ステートメント{$$ = opr(WHILE、2、$ 3、$ 5); }

しかし実際には、トークンを割り当てる必要がある2つの隠れたジャンプがあります。私は次のように意味します:

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を誘発するものは何でしょうか?

4

1 に答える 1

1

何をしようとしているのかは明確ではありませんが、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呼び出しをアセンブルしたときに予約したいくつかのバイト内のジャンプのアドレスを残酷にコピーします。

于 2012-12-31T05:26:31.773 に答える