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

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

今回作成するコンパイラ

ステップ25、文字列リテラルを実装する(文字列リテラルを扱えるコンパイラを作成する)

該当ステップなし(特殊文字を扱えるコンパイラを作成する)

追加・修正されたコンパイラソースコード(ステップ25、文字列リテラルを実装する)

TokenKind

https://github.com/rui314/chibicc/commit/a1e2f21e94f00450075773489cd048dc86af6b4f#diff-d06dbb7ef5899cdf50b340464444680b13aded45363e7aba944dc3551fdf6334R19
https://github.com/rui314/chibicc/blob/a1e2f21e94f00450075773489cd048dc86af6b4f/chibicc.h#L19

// Token
typedef enum {
  TK_RESERVED, // Keywords or punctuators
  TK_IDENT,    // Identifiers
  TK_STR,      // String literals
  TK_NUM,      // Integer literals
  TK_EOF,      // End-of-file markers
} TokenKind;

文字列リテラルを表現するトークン型TK_STRを追加します。

Token構造体

https://github.com/rui314/chibicc/commit/a1e2f21e94f00450075773489cd048dc86af6b4f#diff-d06dbb7ef5899cdf50b340464444680b13aded45363e7aba944dc3551fdf6334R33
https://github.com/rui314/chibicc/blob/a1e2f21e94f00450075773489cd048dc86af6b4f/chibicc.h#L33

// Token type
typedef struct Token Token;
struct Token {
  TokenKind kind; // Token kind
  Token *next;    // Next token
  int val;        // If kind is TK_NUM, its value
  char *str;      // Token string
  int len;        // Token length

  char *contents; // String literal contents including terminating '\0'
  char cont_len;  // string literal length
};

ヌル終端文字列を保存するためのメンバcontentsとそのヌル終端文字列の長さを保存するためのメンバlenを追加します。

tokenize関数

https://github.com/rui314/chibicc/commit/a1e2f21e94f00450075773489cd048dc86af6b4f#diff-289479d6df6940b25dd31a6f2da4881331f916ec642bd1ae47d4ff0a365d8e88R188
https://github.com/rui314/chibicc/blob/a1e2f21e94f00450075773489cd048dc86af6b4f/tokenize.c#L188

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;
    }

    // String literal
    if (*p == '"') {
      char *q = p++;
      while (*p && *p != '"')
        p++;
      if (!*p)
        error_at(q, "unclosed string literal");
      p++;

      cur = new_token(TK_STR, cur, q, p - q);
      cur->contents = strndup(q + 1, p - q - 2);
      cur->cont_len = p - q - 1;
      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;
空白文字の場合(変更なし)
  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;
    }
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;
    }
文字列リテラルの場合
    // String literal
    if (*p == '"') {
      char *q = p++;
      while (*p && *p != '"')
        p++;
      if (!*p)
        error_at(q, "unclosed string literal");
      p++;

      cur = new_token(TK_STR, cur, q, p - q);
      cur->contents = strndup(q + 1, p - q - 2);
      cur->cont_len = p - q - 1;
      continue;
    }

*pp == '"' が真となる場合 → 文字*pが " の場合は、文字列リテラルトークンを生成する処理を行います。


char *q = p++;
文字 " のアドレスをchar型へのポインタqに保存し(文字列の長さを算出する時に使用する)、文字 " の次の文字を得るために後置インクリメントします。


while (*p && *p != '"') p++;
文字*pが、ヌル文字、または、" になるまで、ポインタpをインクリメントします。


if (!*p) error_at(q, "unclosed string literal");
文字*pがヌル文字だった場合は、error_at関数を呼び出して、エラーメッセージを出力します。


p++;
計算の都合上、pをインクリメントします。


cur = new_token(TK_STR, cur, q, p - q);
new_token関数を呼び出して、( " と " を含む)文字列リテラルを表現したトークンを生成します。
(pをインクリメントしなかった場合は、 " と " を含む文字列の長さは、p - q + 1 = (p+1) - q となります)


cur->contents = strndup(q + 1, p - q - 2);
strndup関数を呼び出して、" と " の間にある文字列からなるヌル終端文字列を作成し、文字列リテラルを表現したトークンに保存します。
(pをインクリメントしなかった場合は、 " と " の間にある文字列からなるヌル終端文字列の長さは、p - q +1 - 2 = (p+1) - q - 2 となります)


cur->cont_len = p - q - 1;
contentsに保存した文字列からヌル終端文字の除いた文字列の長さをcont_lenに保存します。
(pをインクリメントしなかった場合は、 contentsに保存した文字列からヌル終端文字の除いた文字列の長さは、p - q +1 - 1 = (p+1) - q - 1 となります)

数字の場合(変更なし)
    // 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;

Var構造体

https://github.com/rui314/chibicc/commit/a1e2f21e94f00450075773489cd048dc86af6b4f#diff-d06dbb7ef5899cdf50b340464444680b13aded45363e7aba944dc3551fdf6334R69
https://github.com/rui314/chibicc/commit/a1e2f21e94f00450075773489cd048dc86af6b4f#diff-d06dbb7ef5899cdf50b340464444680b13aded45363e7aba944dc3551fdf6334R69

// Variable
typedef struct Var Var;
struct Var {
  char *name;    // Variable name
  Type *ty;      // Type
  bool is_local; // local or global

  // Local variable
  int offset;    // Offset from RBP

  // Global variable
  char *contents;
  int cont_len;
};

ヌル終端文字列を保存するメンバcontentsとヌル終端文字列の長さ(ヌル文字を除く)を保存するメンバcont_lenを追加します。

primary関数

https://github.com/rui314/chibicc/commit/a1e2f21e94f00450075773489cd048dc86af6b4f#diff-a07721cd062be25900bddb926de15fc103cf32ea2726d1fea286f6548b810c6aR468
https://github.com/rui314/chibicc/blob/a1e2f21e94f00450075773489cd048dc86af6b4f/parse.c#L468

// primary = "(" expr ")" | "sizeof" unary | ident func-args? | str | num
// args = "(" ident ("," ident)* ")"
Node *primary() {
  Token *tok;

  if (consume("(")) {
    Node *node = expr();
    expect(")");
    return node;
  }

  if (tok = consume("sizeof"))
    return new_unary(ND_SIZEOF, unary(), tok);

  if (tok = consume_ident()) {
    if (consume("(")) {
      Node *node = new_node(ND_FUNCALL, tok);
      node->funcname = strndup(tok->str, tok->len);
      node->args = func_args();
      return node;
    }

    Var *var = find_var(tok);
    if (!var)
      error_tok(tok, "undefined variable");
    return new_var(var, tok);
  }

  tok = token;
  if (tok->kind == TK_STR) {
    token = token->next;

    Type *ty = array_of(char_type(), tok->cont_len);
    Var *var = push_var(new_label(), ty, false);
    var->contents = tok->contents;
    var->cont_len = tok->cont_len;
    return new_var(var, tok);
  }

  if (tok->kind != TK_NUM)
    error_tok(tok, "expected expression");
  return new_num(expect_number(), tok);
}
"("、expr、")"(変更なし)
  if (consume("(")) {
    Node *node = expr();
    expect(")");
    return node;
  }
sizeof、unary(変更なし)
  if (tok = consume("sizeof"))
    return new_unary(ND_SIZEOF, unary(), tok);
ident、func-argsを0回か1回(変更なし)
  if (tok = consume_ident()) {
    if (consume("(")) {
      Node *node = new_node(ND_FUNCALL, tok);
      node->funcname = strndup(tok->str, tok->len);
      node->args = func_args();
      return node;
    }
    Var *var = find_var(tok);
    if (!var)
      error_tok(tok, "undefined variable");
    return new_var(var, tok);
  }
str
  tok = token;
  if (tok->kind == TK_STR) {
    token = token->next;

    Type *ty = array_of(char_type(), tok->cont_len);
    Var *var = push_var(new_label(), ty, false);
    var->contents = tok->contents;
    var->cont_len = tok->cont_len;
    return new_var(var, tok);
  }

トークン型がTK_STR(トークンの種類が文字列リテラル)の場合の処理を追加します。


Type *ty = array_of(char_type(), tok->cont_len);
array_of関数を呼び出して、char型の配列を表現したType構造体を生成します。


Var *var = push_var(new_label(), ty, false);
new_label関数を呼び出して、data領域に保存するために用いるラベル名を生成します。
そのラベル名と先ほど生成したタイプ構造体を用いてpush_var関数を呼び出し、配列名を表現したVar構造体を生成します。
文字列リテラルはdata領域に保存するので、第三引数をfalseにします(グローバル変数と同様の扱いとします)。


var->contents = tok->contents;
var->cont_len = tok->cont_len;
トークンが保有している文字列リテラルと文字列リテラルの長さをVar構造体にコピーします。


return new_var(var, tok);
最後に、 new_var関数を呼び出して、変数(配列名)を表現するノードを生成します。

num(変更なし)
  if (tok->kind != TK_NUM)
    error_tok(tok, "expected expression");
  return new_num(expect_number(), tok);

new_label関数

https://github.com/rui314/chibicc/commit/a1e2f21e94f00450075773489cd048dc86af6b4f#diff-a07721cd062be25900bddb926de15fc103cf32ea2726d1fea286f6548b810c6aR74
https://github.com/rui314/chibicc/blob/a1e2f21e94f00450075773489cd048dc86af6b4f/parse.c#L74

char *new_label() {
  static int cnt = 0;
  char buf[20];
  sprintf(buf, ".L.data.%d", cnt++);
  return strndup(buf, 20);
}

new_label関数は、data領域に保存するために用いるラベル名を生成します。


static int cnt = 0;
new_label関数によって生成されるラベルを識別する番号を定義します。
static修飾子を付けているので、関数の実行が終了した後も、変数cntに値が保存されます。


char buf[20];
sprintf(buf, ".L.data.%d", cnt++);
sprintf関数を呼び出して、第二引数で指定されたラベル名(フォーマットの文字列)を、第一引数で指定されたbufに保存します。


return strndup(buf, 20);
strndup関数を呼び出して、割り当てられたメモリ領域にラベル名(フォーマットの文字列)を保存し、そのメモリ領域のアドレスを戻り値としてリターンします。

emit_data関数

https://github.com/rui314/chibicc/commit/a1e2f21e94f00450075773489cd048dc86af6b4f#diff-629fe11334ae1d560032cdb6cc6f9a4fbb0f5b1365894b6b648d6ee4d5a654beR233
https://github.com/rui314/chibicc/blob/a1e2f21e94f00450075773489cd048dc86af6b4f/codegen.c#L233

void emit_data(Program *prog) {
  printf(".data\n");

  for (VarList *vl = prog->globals; vl; vl = vl->next) {
    Var *var = vl->var;
    printf("%s:\n", var->name);

    if (!var->contents) {
      printf("  .zero %d\n", size_of(var->ty));
      continue;
    }

    for (int i = 0; i < var->cont_len; i++)
      printf("  .byte %d\n", var->contents[i]);
  }
}
データセクションを指定するディレクティブを生成する(変更なし)
 printf(".data\n");
グローバル変数のラベルを生成する(変更なし)
  for (VarList *vl = prog->globals; vl; vl = vl->next) {
    Var *var = vl->var;
    printf("%s:\n", var->name);
グローバル変数の値を初期化する
    if (!var->contents) {
      printf("  .zero %d\n", size_of(var->ty));
      continue;
    }

    for (int i = 0; i < var->cont_len; i++)
      printf("  .byte %d\n", var->contents[i]);
  }

!var->contentsが真となる場合 → var->contentsに文字列リテラルが保存されていない場合は、型のサイズに応じてnバイトのメモリ領域を0で初期化する.zero n ディレクティブを生成します。
!var->contentsが偽となる場合 → var->contentsに文字列リテラルが保存されている場合は、.byteディレクティブと1つの文字データを用いて、1バイトずつ文字列リテラルを定義するアセンブリコードを生成します。

追加・修正されたコンパイラソースコード(該当ステップなし、特殊文字列を扱えるコンパイラを作成する)

tokenize関数

https://github.com/rui314/chibicc/commit/ff171ad6dbfdeddcdaca47364cced2c636c43dca#diff-289479d6df6940b25dd31a6f2da4881331f916ec642bd1ae47d4ff0a365d8e88R232
https://github.com/rui314/chibicc/blob/ff171ad6dbfdeddcdaca47364cced2c636c43dca/tokenize.c#L232

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;
    }

    // String literal
    if (*p == '"') {
      cur = read_string_literal(cur, p);
      p += cur->len;
      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;
空白文字の場合(変更なし)
  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;
    }
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;
    }
文字列リテラルの場合
    // String literal
    if (*p == '"') {
      cur = read_string_literal(cur, p);
      p += cur->len;
      continue;
    }

文字列リテラルを表現したトークンを生成処理をread_string_literal関数に置き換えます。

数字の場合(変更なし)
    // 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;

read_string_literal関数

https://github.com/rui314/chibicc/commit/ff171ad6dbfdeddcdaca47364cced2c636c43dca#diff-289479d6df6940b25dd31a6f2da4881331f916ec642bd1ae47d4ff0a365d8e88R165
https://github.com/rui314/chibicc/blob/ff171ad6dbfdeddcdaca47364cced2c636c43dca/tokenize.c#L165

Token *read_string_literal(Token *cur, char *start) {
  char *p = start + 1;
  char buf[1024];
  int len = 0;

  for (;;) {
    if (len == sizeof(buf))
      error_at(start, "string literal too large");
    if (*p == '\0')
      error_at(start, "unclosed string literal");
    if (*p == '"')
      break;

    if (*p == '\\') {
      p++;
      buf[len++] = get_escape_char(*p++);
    } else {
      buf[len++] = *p++;
    }
  }

  Token *tok = new_token(TK_STR, cur, start, p - start + 1);
  tok->contents = malloc(len + 1);
  memcpy(tok->contents, buf, len);
  tok->contents[len] = '\0';
  tok->cont_len = len + 1;
  return tok;
}

read_string_literal関数は、文字列リテラルを表現するトークンを生成します。


char *p = start + 1;
startは文字列リテラルの先頭にある " を指定するアドレスなので、start+1は " の次にある文字を指定するアドレスとなります。


for文内の処理について
文字列リテラルから読み取った文字*pを配列bufに保存していきます。lenは配列bufのインデックスです。
文字列リテラルから文字を読み取ることができても配列bufにこれ以上文字を保存できない場合(len == sizeof(buf)) が真となる場合)や、文字列リテラルの途中でヌル文字が出現した場合(*p == '\0' が真となる場合)は、error_at関数を呼び出しエラーメッセージを出力します。
文字列リテラルから " を読み取った場合(*p == '"' が真となる場合)は、文字列リテラルの末端に達したとみなし、for文から脱出します。
文字列リテラルから \ を読み取った場合(*p == '\\' が真となる場合)は、 get_escape_char関数を呼び出して、特殊文字を配列bufに保存し、pとlenをインクリメントします(後置インクリメントしています)。
それ以外の場合は、pとlenをインクリメントします。


Token *tok = new_token(TK_STR, cur, start, p - start + 1);以降の処理
new_token関数を呼び出して、( " と " を含む)文字列リテラルを表現したトークンを生成し、文字列リテラルをヌル終端文字列にしてから生成したトークンに保存します。また、生成したトークンに保存した文字列からヌル終端文字の除いた文字列の長さをcont_lenに保存します。

get_escape_char関数

https://github.com/rui314/chibicc/commit/ff171ad6dbfdeddcdaca47364cced2c636c43dca#diff-289479d6df6940b25dd31a6f2da4881331f916ec642bd1ae47d4ff0a365d8e88R150
https://github.com/rui314/chibicc/blob/ff171ad6dbfdeddcdaca47364cced2c636c43dca/tokenize.c#L150

char get_escape_char(char c) {
  switch (c) {
  case 'a': return '\a';
  case 'b': return '\b';
  case 't': return '\t';
  case 'n': return '\n';
  case 'v': return '\v';
  case 'f': return '\f';
  case 'r': return '\r';
  case 'e': return 27;
  case '0': return 0;
  default: return c;
  }
}

get_escape_char関数は、引数cで指定された文字データに応じて、特殊文字を取得します。

テストコード

https://github.com/rui314/chibicc/commit/ff171ad6dbfdeddcdaca47364cced2c636c43dca#diff-3722d9ba8feb2d3feac8ce71a209a638d4b404e1c53f937188761181594023e2R171
https://github.com/rui314/chibicc/blob/ff171ad6dbfdeddcdaca47364cced2c636c43dca/test.sh#L171

#!/bin/bash
cat <<EOF | gcc -xc -c -o tmp2.o -
int ret3() { return 3; }
int ret5() { return 5; }
int add(int x, int y) { return x+y; }
int sub(int x, int y) { return x-y; }
int add6(int a, int b, int c, int d, int e, int f) {
  return a+b+c+d+e+f;
}
EOF

assert() {
  expected="$1"
  input="$2"

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

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

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

assert 0 'int main() { return 0==1; }'
assert 1 'int main() { return 42==42; }'
assert 1 'int main() { return 0!=1; }'
assert 0 'int main() { return 42!=42; }'

assert 1 'int main() { return 0<1; }'
assert 0 'int main() { return 1<1; }'
assert 0 'int main() { return 2<1; }'
assert 1 'int main() { return 0<=1; }'
assert 1 'int main() { return 1<=1; }'
assert 0 'int main() { return 2<=1; }'

assert 1 'int main() { return 1>0; }'
assert 0 'int main() { return 1>1; }'
assert 0 'int main() { return 1>2; }'
assert 1 'int main() { return 1>=0; }'
assert 1 'int main() { return 1>=1; }'
assert 0 'int main() { return 1>=2; }'

assert 3 'int main() { int a; a=3; return a; }'
assert 8 'int main() { int a; int z; a=3; z=5; return a+z; }'
assert 3 'int main() { int a=3; return a; }'
assert 8 'int main() { int a=3; int z=5; return a+z; }'

assert 1 'int main() { return 1; 2; 3; }'
assert 2 'int main() { 1; return 2; 3; }'
assert 3 'int main() { 1; 2; return 3; }'

assert 3 'int main() { int foo=3; return foo; }'
assert 8 'int main() { int foo123=3; int bar=5; return foo123+bar; }'

assert 3 'int main() { if (0) return 2; return 3; }'
assert 3 'int main() { if (1-1) return 2; return 3; }'
assert 2 'int main() { if (1) return 2; return 3; }'
assert 2 'int main() { if (2-1) return 2; return 3; }'

assert 3 'int main() { {1; {2;} return 3;} }'

assert 10 'int main() { int i=0; i=0; while(i<10) i=i+1; return i; }'
assert 55 'int main() { int i=0; int j=0; while(i<=10) {j=i+j; i=i+1;} return j; }'

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

assert 3 'int main() { return ret3(); }'
assert 5 'int main() { return ret5(); }'
assert 8 'int main() { return add(3, 5); }'
assert 2 'int main() { return sub(5, 3); }'
assert 21 'int main() { return add6(1,2,3,4,5,6); }'

assert 32 'int main() { return ret32(); } int ret32() { return 32; }'
assert 7 'int main() { return add2(3,4); } int add2(int x, int y) { return x+y; }'
assert 1 'int main() { return sub2(4,3); } int sub2(int x, int y) { return x-y; }'
assert 55 'int main() { return fib(9); } int fib(int x) { if (x<=1) return 1; return fib(x-1) + fib(x-2); }'

assert 3 'int main() { int x=3; return *&x; }'
assert 3 'int main() { int x=3; int *y=&x; int **z=&y; return **z; }'
assert 5 'int main() { int x=3; int y=5; return *(&x+1); }'
assert 5 'int main() { int x=3; int y=5; return *(1+&x); }'
assert 3 'int main() { int x=3; int y=5; return *(&y-1); }'
assert 5 'int main() { int x=3; int y=5; int *z=&x; return *(z+1); }'
assert 3 'int main() { int x=3; int y=5; int *z=&y; return *(z-1); }'
assert 5 'int main() { int x=3; int *y=&x; *y=5; return x; }'
assert 7 'int main() { int x=3; int y=5; *(&x+1)=7; return y; }'
assert 7 'int main() { int x=3; int y=5; *(&y-1)=7; return x; }'
assert 8 'int main() { int x=3; int y=5; return foo(&x, y); } int foo(int *x, int y) { return *x + y; }'

assert 3 'int main() { int x[2]; int *y=&x; *y=3; return *x; }'

assert 3 'int main() { int x[3]; *x=3; *(x+1)=4; *(x+2)=5; return *x; }'
assert 4 'int main() { int x[3]; *x=3; *(x+1)=4; *(x+2)=5; return *(x+1); }'
assert 5 'int main() { int x[3]; *x=3; *(x+1)=4; *(x+2)=5; return *(x+2); }'

assert 0 'int main() { int x[2][3]; int *y=x; *y=0; return **x; }'
assert 1 'int main() { int x[2][3]; int *y=x; *(y+1)=1; return *(*x+1); }'
assert 2 'int main() { int x[2][3]; int *y=x; *(y+2)=2; return *(*x+2); }'
assert 3 'int main() { int x[2][3]; int *y=x; *(y+3)=3; return **(x+1); }'
assert 4 'int main() { int x[2][3]; int *y=x; *(y+4)=4; return *(*(x+1)+1); }'
assert 5 'int main() { int x[2][3]; int *y=x; *(y+5)=5; return *(*(x+1)+2); }'
assert 6 'int main() { int x[2][3]; int *y=x; *(y+6)=6; return **(x+2); }'

assert 3 'int main() { int x[3]; *x=3; x[1]=4; x[2]=5; return *x; }'
assert 4 'int main() { int x[3]; *x=3; x[1]=4; x[2]=5; return *(x+1); }'
assert 5 'int main() { int x[3]; *x=3; x[1]=4; x[2]=5; return *(x+2); }'
assert 5 'int main() { int x[3]; *x=3; x[1]=4; x[2]=5; return *(x+2); }'
assert 5 'int main() { int x[3]; *x=3; x[1]=4; 2[x]=5; return *(x+2); }'

assert 0 'int main() { int x[2][3]; int *y=x; y[0]=0; return x[0][0]; }'
assert 1 'int main() { int x[2][3]; int *y=x; y[1]=1; return x[0][1]; }'
assert 2 'int main() { int x[2][3]; int *y=x; y[2]=2; return x[0][2]; }'
assert 3 'int main() { int x[2][3]; int *y=x; y[3]=3; return x[1][0]; }'
assert 4 'int main() { int x[2][3]; int *y=x; y[4]=4; return x[1][1]; }'
assert 5 'int main() { int x[2][3]; int *y=x; y[5]=5; return x[1][2]; }'
assert 6 'int main() { int x[2][3]; int *y=x; y[6]=6; return x[2][0]; }'

assert 8 'int main() { int x; return sizeof(x); }'
assert 8 'int main() { int x; return sizeof x; }'
assert 8 'int main() { int *x; return sizeof(x); }'
assert 32 'int main() { int x[4]; return sizeof(x); }'
assert 96 'int main() { int x[3][4]; return sizeof(x); }'
assert 32 'int main() { int x[3][4]; return sizeof(*x); }'
assert 8 'int main() { int x[3][4]; return sizeof(**x); }'
assert 9 'int main() { int x[3][4]; return sizeof(**x) + 1; }'
assert 9 'int main() { int x[3][4]; return sizeof **x + 1; }'
assert 8 'int main() { int x[3][4]; return sizeof(**x + 1); }'

assert 0 'int x; int main() { return x; }'
assert 3 'int x; int main() { x=3; return x; }'
assert 0 'int x[4]; int main() { x[0]=0; x[1]=1; x[2]=2; x[3]=3; return x[0]; }'
assert 1 'int x[4]; int main() { x[0]=0; x[1]=1; x[2]=2; x[3]=3; return x[1]; }'
assert 2 'int x[4]; int main() { x[0]=0; x[1]=1; x[2]=2; x[3]=3; return x[2]; }'
assert 3 'int x[4]; int main() { x[0]=0; x[1]=1; x[2]=2; x[3]=3; return x[3]; }'

assert 8 'int x; int main() { return sizeof(x); }'
assert 32 'int x[4]; int main() { return sizeof(x); }'

assert 1 'int main() { char x=1; return x; }'
assert 1 'int main() { char x=1; char y=2; return x; }'
assert 2 'int main() { char x=1; char y=2; return y; }'

assert 1 'int main() { char x; return sizeof(x); }'
assert 10 'int main() { char x[10]; return sizeof(x); }'
assert 1 'int main() { return sub_char(7, 3, 3); } int sub_char(char a, char b, char c) { return a-b-c; }'

assert 97 'int main() { return "abc"[0]; }'
assert 98 'int main() { return "abc"[1]; }'
assert 99 'int main() { return "abc"[2]; }'
assert 0 'int main() { return "abc"[3]; }'
assert 4 'int main() { return sizeof("abc"); }'

assert 7 'int main() { return "\a"[0]; }'
assert 8 'int main() { return "\b"[0]; }'
assert 9 'int main() { return "\t"[0]; }'
assert 10 'int main() { return "\n"[0]; }'
assert 11 'int main() { return "\v"[0]; }'
assert 12 'int main() { return "\f"[0]; }'
assert 13 'int main() { return "\r"[0]; }'
assert 27 'int main() { return "\e"[0]; }'
assert 0 'int main() { return "\0"[0]; }'

assert 106 'int main() { return "\j"[0]; }'
assert 107 'int main() { return "\k"[0]; }'
assert 108 'int main() { return "\l"[0]; }'

echo OK

Makefile

https://github.com/rui314/chibicc/blob/ff171ad6dbfdeddcdaca47364cced2c636c43dca/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