0

我正在尝试实现一个简单的基础,但恐怕 Bison 的操作对我来说并不清楚。为了实现“while”循环,我发现的示例使用以下结构(或类似结构):

while '(' 表达式 ')' 语句 { $$ = 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 插入条件跳转令牌呢?

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 代码解析为二进制文件,但是你可以加入这两个步骤。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 回答