chibiccを読む~Cコンパイラコードリーディング~ ステップ12

トップページ
jupiteroak.hatenablog.com
「低レイヤを知りたい人のためのCコンパイラ作成入門」のCコンパイラを読んでいきます。
www.sigbus.info
ステップ12に該当
github.com
ステップ12に該当
github.com
ステップ12に該当
github.com

今回作成するコンパイラ

制御構文を足す(if文、while文、for文を扱えるコンパイラを作成する)

追加・修正されたコンパイラソースコード(if文を扱えるコンパイラを作成する)

tokenize関数

https://github.com/rui314/chibicc/commit/ead8dc6996514b7a9b0c82e662bb8dccc830883e#diff-289479d6df6940b25dd31a6f2da4881331f916ec642bd1ae47d4ff0a365d8e88R131
https://github.com/rui314/chibicc/blob/ead8dc6996514b7a9b0c82e662bb8dccc830883e/tokenize.c#L131

Token *tokenize() {
  char *p = user_input;
  Token head;
  head.next = NULL;
  Token *cur = &head;

  while (*p) {
    // Skip whitespace characters.
    if (isspace(*p)) {
      p++;
      continue;
    }

    // Keyword or multi-letter punctuator
    char *kw = starts_with_reserved(p);
    if (kw) {
      int len = strlen(kw);
      cur = new_token(TK_RESERVED, cur, p, len);
      p += len;
      continue;
    }

    // Single-letter punctuator
    if (strchr("+-*/()<>;=", *p)) {
      cur = new_token(TK_RESERVED, cur, p++, 1);
      continue;
    }

    // Identifier
    if (is_alpha(*p)) {
      char *q = p++;
      while (is_alnum(*p))
        p++;
      cur = new_token(TK_IDENT, cur, q, p - q);
      continue;
    }

    // Integer literal
    if (isdigit(*p)) {
      cur = new_token(TK_NUM, cur, p, 0);
      char *q = p;
      cur->val = strtol(p, &p, 10);
      cur->len = p - q;
      continue;
    }

    error_at(p, "invalid token");
  }

  new_token(TK_EOF, cur, p, 0);
  return head.next;
}
文字列の先頭アドレスを取得する(変更なし)
  char *p = user_input;
トークンからなる連結リストのヘッダーを作成する(変更なし)
  Token head;
  head.next = NULL;
  Token *cur = &head;
空白文字の場合(変更なし)
    // Skip whitespace characters.
    if (isspace(*p)) {
      p++;
      continue;
    }
キーワードの場合
    // Keyword or multi-letter punctuator
    char *kw = starts_with_reserved(p);
    if (kw) {
      int len = strlen(kw);
      cur = new_token(TK_RESERVED, cur, p, len);
      p += len;
      continue;
    }

starts_with_reserved関数を呼び出して、文字p*を含んだ文字列 が キーワード や 複数文字の記号 であるかを確認します。
starts_with_reserved関数の戻り値kwがキーワードや複数文字の記号である場合は(文字p*を含んだ文字列がキーワードや複数文字の記号である場合は)、 new_token関数を呼び出して、キーワードを表現するトークンを生成しトークンの連結リストを作成します。変数curには新規に生成されたトークン(連結リストの終端要素)のアドレスが格納されます。
次の文字を取得するためにchar型へのポインタpをlenバイト分(キーワードの長さ文)インクリメントしてからwhile文のループを継続します。

1文字の記号の場合(変更なし)
    // Single-letter punctuator
    if (strchr("+-*/()<>;=", *p)) {
      cur = new_token(TK_RESERVED, cur, p++, 1);
      continue;
    }
識別子の場合(変更なし)
    // Identifier
    if (is_alpha(*p)) {
      char *q = p++;
      while (is_alnum(*p))
        p++;
      cur = new_token(TK_IDENT, cur, q, p - q);
      continue;
    }
数字の場合(変更なし)
    // Integer literal
    if (isdigit(*p)) {
      cur = new_token(TK_NUM, cur, p, 0);
      char *q = p;
      cur->val = strtol(p, &p, 10);
      cur->len = p - q;
      continue;
    }
その他の場合(変更なし)
    error_at(p, "invalid token");
トークン列の終端を表すトークンを生成する(変更なし)
 new_token(TK_EOF, cur, p, 0);
連結リストの先頭トークンを戻り値としてリターンする(変更なし)
return head.next;

starts_with_reserved関数

https://github.com/rui314/chibicc/commit/ead8dc6996514b7a9b0c82e662bb8dccc830883e#diff-289479d6df6940b25dd31a6f2da4881331f916ec642bd1ae47d4ff0a365d8e88R97
https://github.com/rui314/chibicc/blob/ead8dc6996514b7a9b0c82e662bb8dccc830883e/tokenize.c#L97

char *starts_with_reserved(char *p) {
  // Keyword
  static char *kw[] = {"return", "if", "else"};

  for (int i = 0; i < sizeof(kw) / sizeof(*kw); i++) {
    int len = strlen(kw[i]);
    if (startswith(p, kw[i]) && !is_alnum(p[len]))
      return kw[i];
  }

  // Multi-letter punctuator
  static char *ops[] = {"==", "!=", "<=", ">="};

  for (int i = 0; i < sizeof(ops) / sizeof(*ops); i++)
    if (startswith(p, ops[i]))
      return ops[i];

  return NULL;
}

starts_with_reserved関数は、引数pの指定する文字を含む文字列がキーワードや複数文字の記号であるかを判定します。

キーワードを取得する
  // Keyword
  static char *kw[] = {"return", "if", "else"};

  for (int i = 0; i < sizeof(kw) / sizeof(*kw); i++) {
    int len = strlen(kw[i]);
    if (startswith(p, kw[i]) && !is_alnum(p[len]))
      return kw[i];
  }

for文を使って文字p*を含んだ文字列とキーワード(kw配列の要素)を比較します。
sizeof(kw)はkw配列全体のサイズの値(3バイト)、sizeof(*kw)はkw配列要素のサイズの値(1バイト)、となるので、sizeof(kw) / sizeof(*kw)は配列の要素数(3)となります。
startswith(p, kw[i]) && !is_alnum(p[len])が真となる場合 → startswith関数の戻り値がtrue、かつ、is_alnum関数の戻り値がfalseとなる場合 → 文字p*を含んだ文字列とキーワード(kw配列の要素)が一致し、かつ、キーワードの次の文字が英数字ではない場合は、文字p*を含んだ文字列はキーワードとみなせるので、そのキーワードを戻り値としてリターンします。

複数文字の記号を取得する
  // Multi-letter punctuator
  static char *ops[] = {"==", "!=", "<=", ">="};

  for (int i = 0; i < sizeof(ops) / sizeof(*ops); i++)
    if (startswith(p, ops[i]))
      return ops[i];

for文を使って文字p*を含んだ文字列と複数文字の記号(ops配列の要素)を比較します。
startswith(p, ops[i])が真となる場合 → startswith関数の戻り値がtrueとなる場合 → 文字p*を含んだ文字列と複数文字の記号(ops配列の要素)が一致する場合は、その文字列を戻り値としてリターンします。

NodeKind

https://github.com/rui314/chibicc/commit/ead8dc6996514b7a9b0c82e662bb8dccc830883e#diff-d06dbb7ef5899cdf50b340464444680b13aded45363e7aba944dc3551fdf6334R68
https://github.com/rui314/chibicc/blob/ead8dc6996514b7a9b0c82e662bb8dccc830883e/chibicc.h#L68

// AST node
typedef enum {
  ND_ADD,       // +
  ND_SUB,       // -
  ND_MUL,       // *
  ND_DIV,       // /
  ND_EQ,        // ==
  ND_NE,        // !=
  ND_LT,        // <
  ND_LE,        // <=
  ND_ASSIGN,    // =
  ND_RETURN,    // "return"
  ND_IF,        // "if"
  ND_EXPR_STMT, // Expression statement
  ND_VAR,       // Variable
  ND_NUM,       // Integer
} NodeKind;

キーワードifを表現するノード型ND_IFを追加します。

Node構造体

https://github.com/rui314/chibicc/commit/ead8dc6996514b7a9b0c82e662bb8dccc830883e#diff-d06dbb7ef5899cdf50b340464444680b13aded45363e7aba944dc3551fdf6334R83
https://github.com/rui314/chibicc/blob/ead8dc6996514b7a9b0c82e662bb8dccc830883e/chibicc.h#L83

// AST node type
typedef struct Node Node;
struct Node {
  NodeKind kind; // Node kind
  Node *next;    // Next node

  Node *lhs;     // Left-hand side
  Node *rhs;     // Right-hand side

  // "if" statement
  Node *cond;
  Node *then;
  Node *els;

  Var *var;      // Used if kind == ND_VAR
  int val;       // Used if kind == ND_NUM
};

if文をパースする際に使用する子ノードcond、then、elsを追加します。

stmt関数

https://github.com/rui314/chibicc/commit/ead8dc6996514b7a9b0c82e662bb8dccc830883e#diff-a07721cd062be25900bddb926de15fc103cf32ea2726d1fea286f6548b810c6aR95
https://github.com/rui314/chibicc/blob/ead8dc6996514b7a9b0c82e662bb8dccc830883e/parse.c#L95

// stmt = "return" expr ";"
//      | "if" "(" expr ")" stmt ("else" stmt)?
//      | expr ";"
Node *stmt() {
  if (consume("return")) {
    Node *node = new_unary(ND_RETURN, expr());
    expect(";");
    return node;
  }

  if (consume("if")) {
    Node *node = new_node(ND_IF);
    expect("(");
    node->cond = expr();
    expect(")");
    node->then = stmt();
    if (consume("else"))
      node->els = stmt();
    return node;
  }

  Node *node = read_expr_stmt();
  expect(";");
  return node;
}

stmt関数は、生成規則 stmt = "return" expr ";" | "if" "(" expr ")" stmt ("else" stmt)? | expr ";" に基づいて、抽象構文木のノードを生成します。

"return"とexprと";"(変更なし)
  if (consume("return")) {
    Node *node = new_unary(ND_RETURN, expr());
    expect(";");
    return node;
  }
"if"、"("、expr、")"、stmt 、「"else" と stmt」を0回か1回
  if (consume("if")) {
    Node *node = new_node(ND_IF);
    expect("(");
    node->cond = expr();
    expect(")");
    node->then = stmt();
    if (consume("else"))
      node->els = stmt();
    return node;
  }

consume("if")の戻り値がtrueとなる場合→着目しているトークンが"if"の場合は、new_node関数を呼び出してキーワードifを表現するノードを生成します。

ifの次のトークンは"("であることが期待されているのでexpect("(")を呼び出しトークン列を1つ読み進めます。

expr()を呼び出して、条件式に対応する抽象構文木を生成し、その抽象構文木のルートノードを子ノードcond(ifを表現するノードの子ノード)として登録します。

exprの次のトークンは")"であることが期待されているのでexpect(")")を呼び出しトークン列を1つ読み進めます。

stmt()を呼び出して、真文に対応する抽象構文木を生成し、その抽象構文木のルートノードを子ノードthen(ifを表現するノードの子ノード)として登録します。

consume("else")の戻り値がtrueとなる場合→着目しているトークンが"else"の場合は、stmt()を呼び出して、偽文に相当する抽象構文木を生成し、その抽象構文木のルートノードを子ノードels(ifを表現するノードの子ノード)として登録します。

exprと";"
  Node *node = read_expr_stmt();
  expect(";");
  return node;

new_unary(ND_EXPR_STMT, expr())をread_expr_stmt()に変更します。

read_expr_stmt関数

https://github.com/rui314/chibicc/commit/ead8dc6996514b7a9b0c82e662bb8dccc830883e#diff-a07721cd062be25900bddb926de15fc103cf32ea2726d1fea286f6548b810c6aR81
https://github.com/rui314/chibicc/blob/ead8dc6996514b7a9b0c82e662bb8dccc830883e/parse.c#L81

Node *read_expr_stmt() {
  return new_unary(ND_EXPR_STMT, expr());
}

read_expr_stmt関数は、new_unary関数を呼び出して式文を表現するノードを生成します。

gen関数

https://github.com/rui314/chibicc/commit/ead8dc6996514b7a9b0c82e662bb8dccc830883e#diff-629fe11334ae1d560032cdb6cc6f9a4fbb0f5b1365894b6b648d6ee4d5a654beR48
https://github.com/rui314/chibicc/blob/ead8dc6996514b7a9b0c82e662bb8dccc830883e/codegen.c#L48

void gen(Node *node) {
  switch (node->kind) {
  case ND_NUM:
    printf("  push %d\n", node->val);
    return;
  case ND_EXPR_STMT:
    gen(node->lhs);
    printf("  add rsp, 8\n");
    return;
  case ND_VAR:
    gen_addr(node);
    load();
    return;
  case ND_ASSIGN:
    gen_addr(node->lhs);
    gen(node->rhs);
    store();
    return;
  case ND_IF: {
    int seq = labelseq++;
    if (node->els) {
      gen(node->cond);
      printf("  pop rax\n");
      printf("  cmp rax, 0\n");
      printf("  je  .Lelse%d\n", seq);
      gen(node->then);
      printf("  jmp .Lend%d\n", seq);
      printf(".Lelse%d:\n", seq);
      gen(node->els);
      printf(".Lend%d:\n", seq);
    } else {
      gen(node->cond);
      printf("  pop rax\n");
      printf("  cmp rax, 0\n");
      printf("  je  .Lend%d\n", seq);
      gen(node->then);
      printf(".Lend%d:\n", seq);
    }
    return;
  }
  case ND_RETURN:
    gen(node->lhs);
    printf("  pop rax\n");
    printf("  jmp .Lreturn\n");
    return;
  }

  gen(node->lhs);
  gen(node->rhs);

  printf("  pop rdi\n");
  printf("  pop rax\n");

  switch (node->kind) {
  case ND_ADD:
    printf("  add rax, rdi\n");
    break;
  case ND_SUB:
    printf("  sub rax, rdi\n");
    break;
  case ND_MUL:
    printf("  imul rax, rdi\n");
    break;
  case ND_DIV:
    printf("  cqo\n");
    printf("  idiv rdi\n");
    break;
  case ND_EQ:
    printf("  cmp rax, rdi\n");
    printf("  sete al\n");
    printf("  movzb rax, al\n");
    break;
  case ND_NE:
    printf("  cmp rax, rdi\n");
    printf("  setne al\n");
    printf("  movzb rax, al\n");
    break;
  case ND_LT:
    printf("  cmp rax, rdi\n");
    printf("  setl al\n");
    printf("  movzb rax, al\n");
    break;
  case ND_LE:
    printf("  cmp rax, rdi\n");
    printf("  setle al\n");
    printf("  movzb rax, al\n");
    break;
  }

  printf("  push rax\n");
}
二項演算以外を行うアセンブリコードを生成する
  switch (node->kind) {
  case ND_NUM:
    printf("  push %d\n", node->val);
    return;
  case ND_EXPR_STMT:
    gen(node->lhs);
    printf("  add rsp, 8\n");
    return;
  case ND_VAR:
    gen_addr(node);
    load();
    return;
  case ND_ASSIGN:
    gen_addr(node->lhs);
    gen(node->rhs);
    store();
    return;
  case ND_IF: {
    int seq = labelseq++;
    if (node->els) {
      gen(node->cond);
      printf("  pop rax\n");
      printf("  cmp rax, 0\n");
      printf("  je  .Lelse%d\n", seq);
      gen(node->then);
      printf("  jmp .Lend%d\n", seq);
      printf(".Lelse%d:\n", seq);
      gen(node->els);
      printf(".Lend%d:\n", seq);
    } else {
      gen(node->cond);
      printf("  pop rax\n");
      printf("  cmp rax, 0\n");
      printf("  je  .Lend%d\n", seq);
      gen(node->then);
      printf(".Lend%d:\n", seq);
    }
    return;
  }
  case ND_RETURN:
    gen(node->lhs);
    printf("  pop rax\n");
    printf("  jmp .Lreturn\n");
    return;
  }

ノードの型がND_IFの場合(ノードの種類がifの場合)における処理を追加します。


node->elsが真となる場合(else文がある場合)

①gen(node->cond)
条件式の評価値(node->condに対応する値)を得るために必要となるアセンブリコードを生成します。

②printf(" pop rax\n");
条件式の評価値(node->condに対応する値)をスタックから取得するアセンブリコードを生成します。

③printf(" cmp rax, 0\n");printf(" je .Lelse%d\n", seq);
条件式の評価値(node->condに対応する値)が0の場合に、 偽文へジャンプするアセンブリコードを生成します。

④gen(node->then);
真文(node->thenに対応する式文)となるアセンブリコードを生成します。

⑤printf(" jmp .Lend%d\n", seq);
真文を実行した後にジャンプするアセンブリコードを生成します。

⑥printf(" je .Lend%d\n", seq);
偽文が配置されるアドレスに付けるラベルのアセンブリコードを生成します。

⑦gen(node->els);
偽文(node->thenに対応する式文)となるアセンブリコードを生成します。

⑧printf(".Lend%d:\n", seq);
真文を実行した後のジャンプ先となるラベルのアセンブリコードを生成します。


node->elsが偽となる場合(else文がない場合)

①gen(node->cond)
条件式の評価値(node->condに対応する値)を得るために必要となるアセンブリコードを生成します。

②printf(" pop rax\n");
条件式の評価値(node->condに対応する値)をスタックから取得するアセンブリコードを生成します。

③printf(" cmp rax, 0\n");printf(" je .Lend%d\n", seq);
条件式の評価値(node->condに対応する値)が0の場合に、 ジャンプするアセンブリコードを生成します。

④gen(node->then);
真文(node->thenに対応する式文)となるアセンブリコードを生成します。

⑤printf(".Lend%d:\n", seq);
ジャンプ先ラベルのアセンブリコードを生成します。


アセンブリコード内で複数のif文が生成される場合を考慮して、ラベルの識別番号となる変数を定義しておきます。
https://github.com/rui314/chibicc/commit/ead8dc6996514b7a9b0c82e662bb8dccc830883e#diff-629fe11334ae1d560032cdb6cc6f9a4fbb0f5b1365894b6b648d6ee4d5a654beR3
https://github.com/rui314/chibicc/blob/ead8dc6996514b7a9b0c82e662bb8dccc830883e/codegen.c#L3

int labelseq = 0;
二項演算の対象となる値を得るためのアセンブリコードを生成する(変更なし)
  gen(node->lhs);
  gen(node->rhs);

  printf("  pop rdi\n");
  printf("  pop rax\n");
二項演算を行うアセンブリコードを生成する(変更なし)
  switch (node->kind) {
  case ND_ADD:
    printf("  add rax, rdi\n");
    break;
  case ND_SUB:
    printf("  sub rax, rdi\n");
    break;
  case ND_MUL:
    printf("  imul rax, rdi\n");
    break;
  case ND_DIV:
    printf("  cqo\n");
    printf("  idiv rdi\n");
    break;
  case ND_EQ:
    printf("  cmp rax, rdi\n");
    printf("  sete al\n");
    printf("  movzb rax, al\n");
    break;
  case ND_NE:
    printf("  cmp rax, rdi\n");
    printf("  setne al\n");
    printf("  movzb rax, al\n");
    break;
  case ND_LT:
    printf("  cmp rax, rdi\n");
    printf("  setl al\n");
    printf("  movzb rax, al\n");
    break;
  case ND_LE:
    printf("  cmp rax, rdi\n");
    printf("  setle al\n");
    printf("  movzb rax, al\n");
    break;
  }

  printf("  push rax\n");

追加・修正されたコンパイラソースコード(while文を扱えるコンパイラを作成する)

starts_with_reserved関数

https://github.com/rui314/chibicc/commit/a072d39871dbade251c7b9d1d93ecf38c35f0055#diff-289479d6df6940b25dd31a6f2da4881331f916ec642bd1ae47d4ff0a365d8e88R103
https://github.com/rui314/chibicc/blob/a072d39871dbade251c7b9d1d93ecf38c35f0055/tokenize.c#L103

char *starts_with_reserved(char *p) {
  // Keyword
  static char *kw[] = {"return", "if", "else", "while"};

  for (int i = 0; i < sizeof(kw) / sizeof(*kw); i++) {
    int len = strlen(kw[i]);
    if (startswith(p, kw[i]) && !is_alnum(p[len]))
      return kw[i];
  }

  // Multi-letter punctuator
  static char *ops[] = {"==", "!=", "<=", ">="};

  for (int i = 0; i < sizeof(ops) / sizeof(*ops); i++)
    if (startswith(p, ops[i]))
      return ops[i];

  return NULL;
}
キーワードを取得する
  // Keyword
  static char *kw[] = {"return", "if", "else", "while"};

  for (int i = 0; i < sizeof(kw) / sizeof(*kw); i++) {
    int len = strlen(kw[i]);
    if (startswith(p, kw[i]) && !is_alnum(p[len]))
      return kw[i];
  }

配列kwにキーワード"while"を追加します。

複数文字の記号を取得する(変更なし)
  // Multi-letter punctuator
  static char *ops[] = {"==", "!=", "<=", ">="};

  for (int i = 0; i < sizeof(ops) / sizeof(*ops); i++)
    if (startswith(p, ops[i]))
      return ops[i];

  return NULL;

NodeKind

https://github.com/rui314/chibicc/commit/a072d39871dbade251c7b9d1d93ecf38c35f0055#diff-d06dbb7ef5899cdf50b340464444680b13aded45363e7aba944dc3551fdf6334R69
https://github.com/rui314/chibicc/blob/a072d39871dbade251c7b9d1d93ecf38c35f0055/chibicc.h#L69

// AST node
typedef enum {
  ND_ADD,       // +
  ND_SUB,       // -
  ND_MUL,       // *
  ND_DIV,       // /
  ND_EQ,        // ==
  ND_NE,        // !=
  ND_LT,        // <
  ND_LE,        // <=
  ND_ASSIGN,    // =
  ND_RETURN,    // "return"
  ND_IF,        // "if"
  ND_WHILE,     // "while"
  ND_EXPR_STMT, // Expression statement
  ND_VAR,       // Variable
  ND_NUM,       // Integer
} NodeKind;

キーワードwhileを表現するノード型ND_WHILEを追加します。

stmt関数

https://github.com/rui314/chibicc/commit/a072d39871dbade251c7b9d1d93ecf38c35f0055#diff-a07721cd062be25900bddb926de15fc103cf32ea2726d1fea286f6548b810c6aR107
https://github.com/rui314/chibicc/blob/a072d39871dbade251c7b9d1d93ecf38c35f0055/parse.c#L107

Node *stmt() {
  if (consume("return")) {
    Node *node = new_unary(ND_RETURN, expr());
    expect(";");
    return node;
  }

  if (consume("if")) {
    Node *node = new_node(ND_IF);
    expect("(");
    node->cond = expr();
    expect(")");
    node->then = stmt();
    if (consume("else"))
      node->els = stmt();
    return node;
  }

  if (consume("while")) {
    Node *node = new_node(ND_WHILE);
    expect("(");
    node->cond = expr();
    expect(")");
    node->then = stmt();
    return node;
  }

  Node *node = read_expr_stmt();
  expect(";");
  return node;
}

stmt関数は、生成規則 stmt = "return" expr ";" | "if" "(" expr ")" stmt ("else" stmt)? | "while" "(" expr ")" stmt | expr ";" に基づいて、抽象構文木のノードを生成します。

"return"とexprと";"(変更なし)
  if (consume("return")) {
    Node *node = new_unary(ND_RETURN, expr());
    expect(";");
    return node;
  }
"if"、"("、expr、")"、stmt 、「"else" と stmt」を0回か1回(変更なし)
  if (consume("if")) {
    Node *node = new_node(ND_IF);
    expect("(");
    node->cond = expr();
    expect(")");
    node->then = stmt();
    if (consume("else"))
      node->els = stmt();
    return node;
  }
"while"と"("とexprと ")"と stmt
  if (consume("while")) {
    Node *node = new_node(ND_WHILE);
    expect("(");
    node->cond = expr();
    expect(")");
    node->then = stmt();
    return node;
  }

consume("while")の戻り値がtrueとなる場合→着目しているトークンが"while"の場合は、new_node関数を呼び出してキーワードwhileを表現するノードを生成します。

whileの次のトークンは"("であることが期待されているのでexpect("(")を呼び出しトークン列を1つ読み進めます。

expr()を呼び出して、条件式に対応する抽象構文木を生成し、その抽象構文木のルートノードを子ノードcond(whileを表現するノードの子ノード)として登録します。

exprの次のトークンは")"であることが期待されているのでexpect(")")を呼び出しトークン列を1つ読み進めます。

stmt()を呼び出して、真文に対応する抽象構文木を生成し、その抽象構文木のルートノードを子ノードthen(whileを表現するノードの子ノード)として登録します。

exprと";"(変更なし)
  Node *node = read_expr_stmt();
  expect(";");
  return node;

gen関数

https://github.com/rui314/chibicc/commit/a072d39871dbade251c7b9d1d93ecf38c35f0055#diff-629fe11334ae1d560032cdb6cc6f9a4fbb0f5b1365894b6b648d6ee4d5a654beR70
https://github.com/rui314/chibicc/blob/a072d39871dbade251c7b9d1d93ecf38c35f0055/codegen.c#L70

void gen(Node *node) {
  switch (node->kind) {
  case ND_NUM:
    printf("  push %d\n", node->val);
    return;
  case ND_EXPR_STMT:
    gen(node->lhs);
    printf("  add rsp, 8\n");
    return;
  case ND_VAR:
    gen_addr(node);
    load();
    return;
  case ND_ASSIGN:
    gen_addr(node->lhs);
    gen(node->rhs);
    store();
    return;
  case ND_IF: {
    int seq = labelseq++;
    if (node->els) {
      gen(node->cond);
      printf("  pop rax\n");
      printf("  cmp rax, 0\n");
      printf("  je  .Lelse%d\n", seq);
      gen(node->then);
      printf("  jmp .Lend%d\n", seq);
      printf(".Lelse%d:\n", seq);
      gen(node->els);
      printf(".Lend%d:\n", seq);
    } else {
      gen(node->cond);
      printf("  pop rax\n");
      printf("  cmp rax, 0\n");
      printf("  je  .Lend%d\n", seq);
      gen(node->then);
      printf(".Lend%d:\n", seq);
    }
    return;
  }
  case ND_WHILE: {
    int seq = labelseq++;
    printf(".Lbegin%d:\n", seq);
    gen(node->cond);
    printf("  pop rax\n");
    printf("  cmp rax, 0\n");
    printf("  je  .Lend%d\n", seq);
    gen(node->then);
    printf("  jmp .Lbegin%d\n", seq);
    printf(".Lend%d:\n", seq);
    return;
  }
  case ND_RETURN:
    gen(node->lhs);
    printf("  pop rax\n");
    printf("  jmp .Lreturn\n");
    return;
  }

  gen(node->lhs);
  gen(node->rhs);

  printf("  pop rdi\n");
  printf("  pop rax\n");

  switch (node->kind) {
  case ND_ADD:
    printf("  add rax, rdi\n");
    break;
  case ND_SUB:
    printf("  sub rax, rdi\n");
    break;
  case ND_MUL:
    printf("  imul rax, rdi\n");
    break;
  case ND_DIV:
    printf("  cqo\n");
    printf("  idiv rdi\n");
    break;
  case ND_EQ:
    printf("  cmp rax, rdi\n");
    printf("  sete al\n");
    printf("  movzb rax, al\n");
    break;
  case ND_NE:
    printf("  cmp rax, rdi\n");
    printf("  setne al\n");
    printf("  movzb rax, al\n");
    break;
  case ND_LT:
    printf("  cmp rax, rdi\n");
    printf("  setl al\n");
    printf("  movzb rax, al\n");
    break;
  case ND_LE:
    printf("  cmp rax, rdi\n");
    printf("  setle al\n");
    printf("  movzb rax, al\n");
    break;
  }

  printf("  push rax\n");
}
二項演算以外を行うアセンブリコードを生成する
  switch (node->kind) {
  case ND_NUM:
    printf("  push %d\n", node->val);
    return;
  case ND_EXPR_STMT:
    gen(node->lhs);
    printf("  add rsp, 8\n");
    return;
  case ND_VAR:
    gen_addr(node);
    load();
    return;
  case ND_ASSIGN:
    gen_addr(node->lhs);
    gen(node->rhs);
    store();
    return;
  case ND_IF: {
    int seq = labelseq++;
    if (node->els) {
      gen(node->cond);
      printf("  pop rax\n");
      printf("  cmp rax, 0\n");
      printf("  je  .Lelse%d\n", seq);
      gen(node->then);
      printf("  jmp .Lend%d\n", seq);
      printf(".Lelse%d:\n", seq);
      gen(node->els);
      printf(".Lend%d:\n", seq);
    } else {
      gen(node->cond);
      printf("  pop rax\n");
      printf("  cmp rax, 0\n");
      printf("  je  .Lend%d\n", seq);
      gen(node->then);
      printf(".Lend%d:\n", seq);
    }
    return;
  }
  case ND_WHILE: {
    int seq = labelseq++;
    printf(".Lbegin%d:\n", seq);
    gen(node->cond);
    printf("  pop rax\n");
    printf("  cmp rax, 0\n");
    printf("  je  .Lend%d\n", seq);
    gen(node->then);
    printf("  jmp .Lbegin%d\n", seq);
    printf(".Lend%d:\n", seq);
    return;
  }
  case ND_RETURN:
    gen(node->lhs);
    printf("  pop rax\n");
    printf("  jmp .Lreturn\n");
    return;
  }

ノードの型がND_WHILEの場合(ノードの種類がwhileの場合)における処理を追加します。


①printf(".Lbegin%d:\n", seq);
先頭へジャンプする際に使用される(while文を継続する際に使用される)ラベルとなるアセンブリコードを生成します。

②gen(node->cond);
条件式の評価値(node->condの結果)を得るために必要となるアセンブリコードを生成します。

③printf(" pop rax\n");
条件式の評価値(node->condの結果)をスタックから取得するアセンブリコードを生成します。

④printf(" cmp rax, 0\n");printf(" je .Lend%d\n", seq);
条件式の評価値(node->condの結果)が0の場合に、 後尾へジャンプする(while文から脱出する)アセンブリコードを生成します。

⑤gen(node->then);
真文(node->thenに対応する式文)となるアセンブリコードを生成します。

⑥printf(" jmp .Lbegin%d\n", seq);
先頭へジャンプする(while文を継続する)アセンブリコードを生成します。

⑦printf(".Lend%d:\n", seq);
後尾へジャンプする際に使用される(while文から脱出する際に使用される)ラベルとなるアセンブリコードを生成します。

二項演算の対象となる値を得るためのアセンブリコードを生成する(変更なし)
  gen(node->lhs);
  gen(node->rhs);

  printf("  pop rdi\n");
  printf("  pop rax\n");
二項演算を行うアセンブリコードを生成する(変更なし)
  switch (node->kind) {
  case ND_ADD:
    printf("  add rax, rdi\n");
    break;
  case ND_SUB:
    printf("  sub rax, rdi\n");
    break;
  case ND_MUL:
    printf("  imul rax, rdi\n");
    break;
  case ND_DIV:
    printf("  cqo\n");
    printf("  idiv rdi\n");
    break;
  case ND_EQ:
    printf("  cmp rax, rdi\n");
    printf("  sete al\n");
    printf("  movzb rax, al\n");
    break;
  case ND_NE:
    printf("  cmp rax, rdi\n");
    printf("  setne al\n");
    printf("  movzb rax, al\n");
    break;
  case ND_LT:
    printf("  cmp rax, rdi\n");
    printf("  setl al\n");
    printf("  movzb rax, al\n");
    break;
  case ND_LE:
    printf("  cmp rax, rdi\n");
    printf("  setle al\n");
    printf("  movzb rax, al\n");
    break;
  }

  printf("  push rax\n");

追加・修正されたコンパイラソースコード(for文を扱えるコンパイラを作成する)

starts_with_reserved関数

https://github.com/rui314/chibicc/commit/801ee65d8190d69ace65000763a93629a911c14b#diff-289479d6df6940b25dd31a6f2da4881331f916ec642bd1ae47d4ff0a365d8e88R99
https://github.com/rui314/chibicc/blob/801ee65d8190d69ace65000763a93629a911c14b/tokenize.c#L99

char *starts_with_reserved(char *p) {
  // Keyword
  static char *kw[] = {"return", "if", "else", "while", "for"};

  for (int i = 0; i < sizeof(kw) / sizeof(*kw); i++) {
    int len = strlen(kw[i]);
    if (startswith(p, kw[i]) && !is_alnum(p[len]))
      return kw[i];
  }

  // Multi-letter punctuator
  static char *ops[] = {"==", "!=", "<=", ">="};

  for (int i = 0; i < sizeof(ops) / sizeof(*ops); i++)
    if (startswith(p, ops[i]))
      return ops[i];

  return NULL;
}
キーワードを取得する
  // Keyword
  static char *kw[] = {"return", "if", "else", "while", "for"};

  for (int i = 0; i < sizeof(kw) / sizeof(*kw); i++) {
    int len = strlen(kw[i]);
    if (startswith(p, kw[i]) && !is_alnum(p[len]))
      return kw[i];
  }

配列kwにキーワード"for"を追加します。

複数文字の記号を取得する(変更なし)
  // Multi-letter punctuator
  static char *ops[] = {"==", "!=", "<=", ">="};

  for (int i = 0; i < sizeof(ops) / sizeof(*ops); i++)
    if (startswith(p, ops[i]))
      return ops[i];

  return NULL;

NodeKind

https://github.com/rui314/chibicc/commit/801ee65d8190d69ace65000763a93629a911c14b#diff-d06dbb7ef5899cdf50b340464444680b13aded45363e7aba944dc3551fdf6334R70
https://github.com/rui314/chibicc/blob/801ee65d8190d69ace65000763a93629a911c14b/chibicc.h#L70

// AST node
typedef enum {
  ND_ADD,       // +
  ND_SUB,       // -
  ND_MUL,       // *
  ND_DIV,       // /
  ND_EQ,        // ==
  ND_NE,        // !=
  ND_LT,        // <
  ND_LE,        // <=
  ND_ASSIGN,    // =
  ND_RETURN,    // "return"
  ND_IF,        // "if"
  ND_WHILE,     // "while"
  ND_FOR,       // "for"
  ND_EXPR_STMT, // Expression statement
  ND_VAR,       // Variable
  ND_NUM,       // Integer
} NodeKind;

キーワードforを表現するノード型ND_FORを追加します。

Node構造体

https://github.com/rui314/chibicc/commit/801ee65d8190d69ace65000763a93629a911c14b#diff-d06dbb7ef5899cdf50b340464444680b13aded45363e7aba944dc3551fdf6334R85
https://github.com/rui314/chibicc/blob/801ee65d8190d69ace65000763a93629a911c14b/chibicc.h#L85

// AST node type
typedef struct Node Node;
struct Node {
  NodeKind kind; // Node kind
  Node *next;    // Next node

  Node *lhs;     // Left-hand side
  Node *rhs;     // Right-hand side

  // "if, "while" or "for" statement
  Node *cond;
  Node *then;
  Node *els;
  Node *init;
  Node *inc;

  Var *var;      // Used if kind == ND_VAR
  int val;       // Used if kind == ND_NUM
};

for文をパースする際に使用する子ノードinit、incを追加します。

stmt関数

https://github.com/rui314/chibicc/commit/801ee65d8190d69ace65000763a93629a911c14b#diff-a07721cd062be25900bddb926de15fc103cf32ea2726d1fea286f6548b810c6aR117
https://github.com/rui314/chibicc/blob/801ee65d8190d69ace65000763a93629a911c14b/parse.c#L117

Node *stmt() {
  if (consume("return")) {
    Node *node = new_unary(ND_RETURN, expr());
    expect(";");
    return node;
  }

  if (consume("if")) {
    Node *node = new_node(ND_IF);
    expect("(");
    node->cond = expr();
    expect(")");
    node->then = stmt();
    if (consume("else"))
      node->els = stmt();
    return node;
  }

  if (consume("while")) {
    Node *node = new_node(ND_WHILE);
    expect("(");
    node->cond = expr();
    expect(")");
    node->then = stmt();
    return node;
  }

  if (consume("for")) {
    Node *node = new_node(ND_FOR);
    expect("(");
    if (!consume(";")) {
      node->init = read_expr_stmt();
      expect(";");
    }
    if (!consume(";")) {
      node->cond = expr();
      expect(";");
    }
    if (!consume(")")) {
      node->inc = read_expr_stmt();
      expect(")");
    }
    node->then = stmt();
    return node;
  }

  Node *node = read_expr_stmt();
  expect(";");
  return node;
}

stmt関数は、生成規則 stmt = "return" expr ";" | "if" "(" expr ")" stmt ("else" stmt)? | "while" "(" expr ")" stmt | "for" "(" expr? ";" expr? ";" expr? ")" stmt | expr ";" に基づいて、抽象構文木のノードを生成します。

"return"とexprと";"(変更なし)
  if (consume("return")) {
    Node *node = new_unary(ND_RETURN, expr());
    expect(";");
    return node;
  }
"if"、"("、expr、")"、stmt 、「"else" と stmt」を0回か1回(変更なし)
  if (consume("if")) {
    Node *node = new_node(ND_IF);
    expect("(");
    node->cond = expr();
    expect(")");
    node->then = stmt();
    if (consume("else"))
      node->els = stmt();
    return node;
  }
"while"と"("とexprと ")"と stmt(変更なし)
  if (consume("while")) {
    Node *node = new_node(ND_WHILE);
    expect("(");
    node->cond = expr();
    expect(")");
    node->then = stmt();
    return node;
  }
"for"、"("、exprを0回か1回、 ";"、exprを0回か1回、";"、exprを0回か1回、")"、stmt
  if (consume("for")) {
    Node *node = new_node(ND_FOR);
    expect("(");
    if (!consume(";")) {
      node->init = read_expr_stmt();
      expect(";");
    }
    if (!consume(";")) {
      node->cond = expr();
      expect(";");
    }
    if (!consume(")")) {
      node->inc = read_expr_stmt();
      expect(")");
    }
    node->then = stmt();
    return node;
  }

consume("for")の戻り値がtrueとなる場合→着目しているトークンが"for"の場合は、new_node関数を呼び出してキーワードforを表現するノードを生成します。
forの次のトークンは"("であることが期待されているのでexpect("(")を呼び出しトークン列を1つ読み進めます。

consume(";")の戻り値がfalseとなる場合→初期設定式のトークン列がある場合は、read_expr_stmt関数を呼び出して式文(初期設定式)に対応する抽象構文木を生成し、その抽象構文木のルートノードを子ノードinit(forを表現するノードの子ノード)として登録します。
初期設定式のトークン列の次のトークンは")"であることが期待されているのでexpect(")")を呼び出しトークン列を1つ読み進めます。

consume(";")の戻り値がfalseとなる場合→継続条件式のトークン列がある場合は、expr関数を呼び出して式(継続条件式)に対応する抽象構文木を生成し、その抽象構文木のルートノードを子ノードcond(forを表現するノードの子ノード)として登録します。
継続条件式のトークン列の次のトークンは")"であることが期待されているのでexpect(")")を呼び出しトークン列を1つ読み進めます。

consume(";")の戻り値がfalseとなる場合→再設定式のトークン列がある場合は、read_expr_stmt関数を呼び出して式文(再設定式)に対応する抽象構文木を生成し、その抽象構文木のルートノードを子ノードinc(forを表現するノードの子ノード)として登録します。
再設定式のトークン列の次のトークンは")"であることが期待されているのでexpect(")")を呼び出しトークン列を1つ読み進めます。

最後に、stmt()を呼び出して真文に対応する抽象構文木を生成し、その抽象構文木のルートノードを子ノードthen(forを表現するノードの子ノード)として登録します。

exprと";"(変更なし)
  Node *node = read_expr_stmt();
  expect(";");
  return node;

gen関数

https://github.com/rui314/chibicc/commit/801ee65d8190d69ace65000763a93629a911c14b#diff-629fe11334ae1d560032cdb6cc6f9a4fbb0f5b1365894b6b648d6ee4d5a654beR82
https://github.com/rui314/chibicc/blob/801ee65d8190d69ace65000763a93629a911c14b/codegen.c#L82

 void gen(Node *node) {
  switch (node->kind) {
  case ND_NUM:
    printf("  push %d\n", node->val);
    return;
  case ND_EXPR_STMT:
    gen(node->lhs);
    printf("  add rsp, 8\n");
    return;
  case ND_VAR:
    gen_addr(node);
    load();
    return;
  case ND_ASSIGN:
    gen_addr(node->lhs);
    gen(node->rhs);
    store();
    return;
  case ND_IF: {
    int seq = labelseq++;
    if (node->els) {
      gen(node->cond);
      printf("  pop rax\n");
      printf("  cmp rax, 0\n");
      printf("  je  .Lelse%d\n", seq);
      gen(node->then);
      printf("  jmp .Lend%d\n", seq);
      printf(".Lelse%d:\n", seq);
      gen(node->els);
      printf(".Lend%d:\n", seq);
    } else {
      gen(node->cond);
      printf("  pop rax\n");
      printf("  cmp rax, 0\n");
      printf("  je  .Lend%d\n", seq);
      gen(node->then);
      printf(".Lend%d:\n", seq);
    }
    return;
  }
  case ND_WHILE: {
    int seq = labelseq++;
    printf(".Lbegin%d:\n", seq);
    gen(node->cond);
    printf("  pop rax\n");
    printf("  cmp rax, 0\n");
    printf("  je  .Lend%d\n", seq);
    gen(node->then);
    printf("  jmp .Lbegin%d\n", seq);
    printf(".Lend%d:\n", seq);
    return;
  }
  case ND_FOR: {
    int seq = labelseq++;
    if (node->init)
      gen(node->init);
    printf(".Lbegin%d:\n", seq);
    if (node->cond) {
      gen(node->cond);
      printf("  pop rax\n");
      printf("  cmp rax, 0\n");
      printf("  je  .Lend%d\n", seq);
    }
    gen(node->then);
    if (node->inc)
      gen(node->inc);
    printf("  jmp .Lbegin%d\n", seq);
    printf(".Lend%d:\n", seq);
    return;
  }
  case ND_RETURN:
    gen(node->lhs);
    printf("  pop rax\n");
    printf("  jmp .Lreturn\n");
    return;
  }

  gen(node->lhs);
  gen(node->rhs);

  printf("  pop rdi\n");
  printf("  pop rax\n");

  switch (node->kind) {
  case ND_ADD:
    printf("  add rax, rdi\n");
    break;
  case ND_SUB:
    printf("  sub rax, rdi\n");
    break;
  case ND_MUL:
    printf("  imul rax, rdi\n");
    break;
  case ND_DIV:
    printf("  cqo\n");
    printf("  idiv rdi\n");
    break;
  case ND_EQ:
    printf("  cmp rax, rdi\n");
    printf("  sete al\n");
    printf("  movzb rax, al\n");
    break;
  case ND_NE:
    printf("  cmp rax, rdi\n");
    printf("  setne al\n");
    printf("  movzb rax, al\n");
    break;
  case ND_LT:
    printf("  cmp rax, rdi\n");
    printf("  setl al\n");
    printf("  movzb rax, al\n");
    break;
  case ND_LE:
    printf("  cmp rax, rdi\n");
    printf("  setle al\n");
    printf("  movzb rax, al\n");
    break;
  }

  printf("  push rax\n");
}
二項演算以外を行うアセンブリコードを生成する
  switch (node->kind) {
  case ND_NUM:
    printf("  push %d\n", node->val);
    return;
  case ND_EXPR_STMT:
    gen(node->lhs);
    printf("  add rsp, 8\n");
    return;
  case ND_VAR:
    gen_addr(node);
    load();
    return;
  case ND_ASSIGN:
    gen_addr(node->lhs);
    gen(node->rhs);
    store();
    return;
  case ND_IF: {
    int seq = labelseq++;
    if (node->els) {
      gen(node->cond);
      printf("  pop rax\n");
      printf("  cmp rax, 0\n");
      printf("  je  .Lelse%d\n", seq);
      gen(node->then);
      printf("  jmp .Lend%d\n", seq);
      printf(".Lelse%d:\n", seq);
      gen(node->els);
      printf(".Lend%d:\n", seq);
    } else {
      gen(node->cond);
      printf("  pop rax\n");
      printf("  cmp rax, 0\n");
      printf("  je  .Lend%d\n", seq);
      gen(node->then);
      printf(".Lend%d:\n", seq);
    }
    return;
  }
  case ND_WHILE: {
    int seq = labelseq++;
    printf(".Lbegin%d:\n", seq);
    gen(node->cond);
    printf("  pop rax\n");
    printf("  cmp rax, 0\n");
    printf("  je  .Lend%d\n", seq);
    gen(node->then);
    printf("  jmp .Lbegin%d\n", seq);
    printf(".Lend%d:\n", seq);
    return;
  }
  case ND_FOR: {
    int seq = labelseq++;
    if (node->init)
      gen(node->init);
    printf(".Lbegin%d:\n", seq);
    if (node->cond) {
      gen(node->cond);
      printf("  pop rax\n");
      printf("  cmp rax, 0\n");
      printf("  je  .Lend%d\n", seq);
    }
    gen(node->then);
    if (node->inc)
      gen(node->inc);
    printf("  jmp .Lbegin%d\n", seq);
    printf(".Lend%d:\n", seq);
    return;
  }
  case ND_RETURN:
    gen(node->lhs);
    printf("  pop rax\n");
    printf("  jmp .Lreturn\n");
    return;
  }

ノードの型がND_FORの場合(ノードの種類がforの場合)における処理を追加します。


①gen(node->init);
node->initが真となる場合 → 初期設定式に対応する抽象構文木がある場合は、gen関数を呼び出して初期設定式(node->initに対応する式文)となるアセンブリコードを生成します。

②printf(".Lbegin%d:\n", seq);
先頭へジャンプする際に使用される(for文を継続する際に使用される)ラベルを生成します。

③gen(node->cond);
node->condが真となる場合 → 継続条件式に対応する抽象構文木がある場合は、gen関数を呼び出して継続条件式の評価値(node->condの結果)を得るために必要なアセンブリコードを生成します。

④printf(" pop rax\n");
継続条件式の評価値(node->condの結果)をスタックから得るためのアセンブリコードを生成します。

⑤printf(" cmp rax, 0\n");printf(" je .Lend%d\n", seq);
継続条件式の評価値(node->condの結果)が0の場合に、後尾へジャンプする(for文から脱出する)アセンブリコードを生成します。

⑥gen(node->then);
真文(node->thenに対応する式文)となるアセンブリコードを生成します。

⑦gen(node->inc);
node->incが真となる場合 → 再設定式に対応する抽象構文木がある場合は、gen関数を呼び出して再設定式(node->incに対応する式文)となるアセンブリコードを生成します。

⑧printf(" jmp .Lbegin%d\n", seq);
先頭へジャンプする(for文を継続する)アセンブリコードを生成します。

⑨printf(".Lend%d:\n", seq);
後尾へジャンプする際に使用される(for文を脱出する際に使用される)ラベルを生成します。

二項演算の対象となる値を得るためのアセンブリコードを生成する(変更なし)
  gen(node->lhs);
  gen(node->rhs);

  printf("  pop rdi\n");
  printf("  pop rax\n");
二項演算を行うアセンブリコードを生成する(変更なし)
  switch (node->kind) {
  case ND_ADD:
    printf("  add rax, rdi\n");
    break;
  case ND_SUB:
    printf("  sub rax, rdi\n");
    break;
  case ND_MUL:
    printf("  imul rax, rdi\n");
    break;
  case ND_DIV:
    printf("  cqo\n");
    printf("  idiv rdi\n");
    break;
  case ND_EQ:
    printf("  cmp rax, rdi\n");
    printf("  sete al\n");
    printf("  movzb rax, al\n");
    break;
  case ND_NE:
    printf("  cmp rax, rdi\n");
    printf("  setne al\n");
    printf("  movzb rax, al\n");
    break;
  case ND_LT:
    printf("  cmp rax, rdi\n");
    printf("  setl al\n");
    printf("  movzb rax, al\n");
    break;
  case ND_LE:
    printf("  cmp rax, rdi\n");
    printf("  setle al\n");
    printf("  movzb rax, al\n");
    break;
  }

  printf("  push rax\n");
}

テストコード

https://github.com/rui314/chibicc/blob/801ee65d8190d69ace65000763a93629a911c14b/test.sh

#!/bin/bash
assert() {
  expected="$1"
  input="$2"

  ./chibicc "$input" > tmp.s
  gcc -static -o tmp tmp.s
  ./tmp
  actual="$?"

  if [ "$actual" = "$expected" ]; then
    echo "$input => $actual"
  else
    echo "$input => $expected expected, but got $actual"
    exit 1
  fi
}

assert 0 'return 0;'
assert 42 'return 42;'
assert 21 'return 5+20-4;'
assert 41 'return  12 + 34 - 5 ;'
assert 47 'return 5+6*7;'
assert 15 'return 5*(9-6);'
assert 4 'return (3+5)/2;'
assert 10 'return -10+20;'
assert 10 'return - -10;'
assert 10 'return - - +10;'

assert 0 'return 0==1;'
assert 1 'return 42==42;'
assert 1 'return 0!=1;'
assert 0 'return 42!=42;'

assert 1 'return 0<1;'
assert 0 'return 1<1;'
assert 0 'return 2<1;'
assert 1 'return 0<=1;'
assert 1 'return 1<=1;'
assert 0 'return 2<=1;'

assert 1 'return 1>0;'
assert 0 'return 1>1;'
assert 0 'return 1>2;'
assert 1 'return 1>=0;'
assert 1 'return 1>=1;'
assert 0 'return 1>=2;'

assert 3 'a=3; return a;'
assert 8 'a=3; z=5; return a+z;'

assert 1 'return 1; 2; 3;'
assert 2 '1; return 2; 3;'
assert 3 '1; 2; return 3;'

assert 3 'foo=3; return foo;'
assert 8 'foo123=3; bar=5; return foo123+bar;'

assert 3 'if (0) return 2; return 3;'
assert 3 'if (1-1) return 2; return 3;'
assert 2 'if (1) return 2; return 3;'
assert 2 'if (2-1) return 2; return 3;'

assert 10 'i=0; while(i<10) i=i+1; return i;'

assert 55 'i=0; j=0; for (i=0; i<=10; i=i+1) j=i+j; return j;'
assert 3 'for (;;) return 3; return 5;'

echo OK

Makefile

https://github.com/rui314/chibicc/blob/801ee65d8190d69ace65000763a93629a911c14b/Makefile

CFLAGS=-std=c11 -g -static
SRCS=$(wildcard *.c)
OBJS=$(SRCS:.c=.o)

chibicc: $(OBJS)
	$(CC) -o $@ $(OBJS) $(LDFLAGS)

$(OBJS): chibicc.h

test: chibicc
	./test.sh

clean:
	rm -f chibicc *.o *~ tmp*

.PHONY: test clean