diff --git a/chibi.h b/chibi.h index 8743925..c234a68 100644 --- a/chibi.h +++ b/chibi.h @@ -123,6 +123,8 @@ typedef enum { ND_BLOCK, // { ... } ND_BREAK, // "break" ND_CONTINUE, // "continue" + ND_GOTO, // "goto" + ND_LABEL, // Labeled statement ND_FUNCALL, // Function call ND_EXPR_STMT, // Expression statement ND_STMT_EXPR, // Statement expression @@ -160,6 +162,9 @@ struct Node { char *funcname; Node *args; + // Goto or labeled statement + char *label_name; + Var *var; // Used if kind == ND_VAR long val; // Used if kind == ND_NUM }; diff --git a/codegen.c b/codegen.c index a06b588..f89ce29 100644 --- a/codegen.c +++ b/codegen.c @@ -401,6 +401,13 @@ static void gen(Node *node) { error_tok(node->tok, "stray continue"); printf(" jmp .L.continue.%d\n", contseq); return; + case ND_GOTO: + printf(" jmp .L.label.%s.%s\n", funcname, node->label_name); + return; + case ND_LABEL: + printf(".L.label.%s.%s:\n", funcname, node->label_name); + gen(node->lhs); + return; case ND_FUNCALL: { int nargs = 0; for (Node *arg = node->args; arg; arg = arg->next) { diff --git a/parse.c b/parse.c index 7279929..986c623 100644 --- a/parse.c +++ b/parse.c @@ -719,6 +719,8 @@ static Node *stmt(void) { // | "{" stmt* "}" // | "break" ";" // | "continue" ";" +// | "goto" ident ";" +// | ident ":" stmt // | declaration // | expr ";" static Node *stmt2(void) { @@ -802,6 +804,22 @@ static Node *stmt2(void) { return new_node(ND_CONTINUE, tok); } + if (tok = consume("goto")) { + Node *node = new_node(ND_GOTO, tok); + node->label_name = expect_ident(); + expect(";"); + return node; + } + + if (tok = consume_ident()) { + if (consume(":")) { + Node *node = new_unary(ND_LABEL, stmt(), tok); + node->label_name = strndup(tok->str, tok->len); + return node; + } + token = tok; + } + if (is_typename()) return declaration(); diff --git a/tests b/tests index 3deacdc..8d5481e 100644 --- a/tests +++ b/tests @@ -438,6 +438,10 @@ int main() { assert(5, ({ int i=0; int j=0; while (i++<10) { if (i>5) continue; j++; } j; }), "int i=0; int j=0; while (i++<10) { if (i>5) continue; j++; } j;"); assert(11, ({ int i=0; int j=0; while(!i) { while (j++!=10) continue; break; } j; }), "int i=0; int j=0; while(!i) { while (j++!=10) continue; break; } j;"); + assert(3, ({ int i=0; goto a; a: i++; b: i++; c: i++; i; }), "int i=0; goto a; a: i++; b: i++; c: i++; i;"); + assert(2, ({ int i=0; goto e; d: i++; e: i++; f: i++; i; }), "int i=0; goto d; d: i++; e: i++; f: i++; i;"); + assert(1, ({ int i=0; goto i; g: i++; h: i++; i: i++; i; }), "int i=0; goto g; h: i++; i: i++; j: i++; i;"); + printf("OK\n"); return 0; } diff --git a/tokenize.c b/tokenize.c index 4931b28..845d575 100644 --- a/tokenize.c +++ b/tokenize.c @@ -150,7 +150,7 @@ static char *starts_with_reserved(char *p) { static char *kw[] = {"return", "if", "else", "while", "for", "int", "char", "sizeof", "struct", "typedef", "short", "long", "void", "_Bool", "enum", "static", "break", - "continue"}; + "continue", "goto"}; for (int i = 0; i < sizeof(kw) / sizeof(*kw); i++) { int len = strlen(kw[i]);