udem-dlteam / pnut

🥜 A Self-Compiling C Transpiler Targeting Human-Readable POSIX Shell
https://pnut.sh
BSD 2-Clause "Simplified" License
425 stars 14 forks source link

Support switch statements #18

Closed laurenthuberdeau closed 6 months ago

laurenthuberdeau commented 6 months ago

Context

TCC uses switch statements, so we support them.

Shell backend

Because we compile C switch statements to Shell case statement, fall through from a conditional block to another is not supported as this is not possible in Shell case statements. If we really wanted to, it could be done by copying the code between conditional blocks, but it would go against our goal of readability and matching the original C code.

laurenthuberdeau commented 6 months ago

Testing

I rewrote a bunch of if/elseif code in pnut to use a switch instead and it still bootstrapped. It didn't improve the quality of the C code that much, so I decided to not include the changes in this PR, but it shows that basic switch statements are supported.

I then used the following code to test certain corner cases:

void putstr(char* s) {
  while (*s) {
    putchar(*s);
    s++;
  }
}

void test(int i) {
  switch (i) {
    case 1:
      putstr("one\n");
      break;
    case 2:
      putstr("two\n");
      break;
    case 3:
      putstr("three\n");
      break;
    case 4:
      putstr("four\n");
      break;
    case 5:
    case 6:
    case 7:
    case 8:
      putstr("five to eight or ");
    case 9:
      putstr("nine\n");
      break;
    default:
      putstr("other\n");
  }
}

void loop() {
  // A while loop with a switch in it
  int i = 0;
  while (i < 7) {
    switch (i) {
      case 0:
        i++;
        putstr("i = 0 => break. i is increment again and i = 2 on next iteration\n");
        break;
      case 1:
        putstr("one\n");
        break;
      case 2:
        putstr("two\n");
        break;
      case 3:
        i++;
        putstr("i = 3 => continue. i is not incremented again and i = 4 on next iteration\n");
        continue;
      case 4:
        putstr("four\n");
        break;
      case 5:
        putstr("five\n");
        break;
      default:
        putstr("other\n");
    }
    i++;
  }
}

void main(int argc) {
  int i;
  putstr("# Testing fallthrough between cases in switch:\n");
  for (i = 0; i < 12; i++) {
    test(i);
  }

  putstr("# Testing break/continue in switch in while loop:\n");
  loop();
}

which produces the following output, both using gcc and pnut:

# Testing fallthrough between cases in switch:
other
one
two
three
four
five to eight or nine
five to eight or nine
five to eight or nine
five to eight or nine
nine
other
other
# Testing break/continue in switch in while loop:
i = 0 => break. i is increment again and i = 2 on next iteration
two
i = 3 => continue. i is not incremented again and i = 4 on next iteration
four
five
other